Skip to content

Commit 629edc1

Browse files
authored
Merge pull request google#683 from google/python_styleguide
Project import generated by Copybara. Mostly modernizing to a 3.9+ Pythonisms. Some clarification on TypeVars.
2 parents af78b49 + d2ca0a8 commit 629edc1

File tree

1 file changed

+84
-55
lines changed

1 file changed

+84
-55
lines changed

pyguide.md

Lines changed: 84 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ that the arguments are actually unused.
213213

214214
Use `import` statements for packages and modules only, not for individual
215215
classes or functions. Classes imported from the
216-
[typing module](#typing-imports),
217-
[typing_extensions module](https://github.com/python/typing/tree/master/typing_extensions),
216+
[`typing` module](#typing-imports), [`collections.abc` module](#typing-imports),
217+
[`typing_extensions` module](https://github.com/python/typing/tree/master/typing_extensions),
218218
and redirects from the
219219
[six.moves module](https://six.readthedocs.io/#module-six.moves)
220220
are exempt from this rule.
@@ -254,8 +254,9 @@ Module names can still collide. Some module names are inconveniently long.
254254
* Use `import x` for importing packages and modules.
255255
* Use `from x import y` where `x` is the package prefix and `y` is the module
256256
name with no prefix.
257-
* Use `from x import y as z` if two modules named `y` are to be imported or if
258-
`y` is an inconveniently long name.
257+
* Use `from x import y as z` if two modules named `y` are to be imported, if
258+
`y` conflicts with a top-level name defined in the current module, or if `y`
259+
is an inconveniently long name.
259260
* Use `import y as z` only when `z` is a standard abbreviation (e.g., `np` for
260261
`numpy`).
261262

@@ -313,7 +314,7 @@ Yes:
313314
import absl.flags
314315
from doctor.who import jodie
315316

316-
FLAGS = absl.flags.FLAGS
317+
_FOO = absl.flags.DEFINE_string(...)
317318
```
318319

319320
```python
@@ -322,7 +323,7 @@ Yes:
322323
from absl import flags
323324
from doctor.who import jodie
324325

325-
FLAGS = flags.FLAGS
326+
_FOO = flags.DEFINE_string(...)
326327
```
327328

328329
*(assume this file lives in `doctor/who/` where `jodie.py` also exists)*
@@ -416,7 +417,7 @@ Exceptions must follow certain conditions:
416417
# guarantee this specific behavioral reaction to API misuse.
417418
raise ValueError(f'Min. port must be at least 1024, not {minimum}.')
418419
port = self._find_next_open_port(minimum)
419-
if not port:
420+
if port is None:
420421
raise ConnectionError(
421422
f'Could not connect to service on port {minimum} or higher.')
422423
assert port >= minimum, (
@@ -509,13 +510,14 @@ assignments to global variables are done when the module is first imported.
509510

510511
Avoid global variables.
511512

512-
While they are technically variables, module-level constants are permitted and
513-
encouraged. For example: `_MAX_HOLY_HANDGRENADE_COUNT = 3`. Constants must be
514-
named using all caps with underscores. See [Naming](#s3.16-naming) below.
513+
If needed, global variables should be declared at the module level and made
514+
internal to the module by prepending an `_` to the name. External access to
515+
global variables must be done through public module-level functions. See
516+
[Naming](#s3.16-naming) below.
515517

516-
If needed, globals should be declared at the module level and made internal to
517-
the module by prepending an `_` to the name. External access must be done
518-
through public module-level functions. See [Naming](#s3.16-naming) below.
518+
While module-level constants are technically variables, they are permitted and
519+
encouraged. For example: `MAX_HOLY_HANDGRENADE_COUNT = 3`. Constants must be
520+
named using all caps with underscores. See [Naming](#s3.16-naming) below.
519521

520522
<a id="s2.6-nested"></a>
521523
<a id="26-nested"></a>
@@ -955,11 +957,14 @@ Yes: def foo(a, b: Sequence = ()): # Empty tuple OK since tuples are immutable
955957
```
956958
957959
```python
960+
from absl import flags
961+
_FOO = flags.DEFINE_string(...)
962+
958963
No: def foo(a, b=[]):
959964
...
960965
No: def foo(a, b=time.time()): # The time the module was loaded???
961966
...
962-
No: def foo(a, b=FLAGS.my_thing): # sys.argv has not yet been parsed...
967+
No: def foo(a, b=_FOO.value): # sys.argv has not yet been parsed...
963968
...
964969
No: def foo(a, b: Mapping = {}): # Could still get passed to unchecked code
965970
...
@@ -1471,7 +1476,7 @@ Type annotations (or "type hints") are for function or method arguments and
14711476
return values:
14721477
14731478
```python
1474-
def func(a: int) -> List[int]:
1479+
def func(a: int) -> list[int]:
14751480
```
14761481
14771482
You can also declare the type of a variable using similar
@@ -2026,7 +2031,7 @@ aptly described using a one-line docstring.
20262031
([example](http://numpy.org/doc/stable/reference/generated/numpy.linalg.qr.html)),
20272032
which frequently documents a tuple return value as if it were multiple
20282033
return values with individual names (never mentioning the tuple). Instead,
2029-
describe such a return value as: "Returns a tuple (mat_a, mat_b), where
2034+
describe such a return value as: "Returns: A tuple (mat_a, mat_b), where
20302035
mat_a is ..., and ...". The auxiliary names in the docstring need not
20312036
necessarily correspond to any internal names used in the function body (as
20322037
those are not part of the API).
@@ -2044,7 +2049,7 @@ aptly described using a one-line docstring.
20442049
def fetch_smalltable_rows(table_handle: smalltable.Table,
20452050
keys: Sequence[Union[bytes, str]],
20462051
require_all_keys: bool = False,
2047-
) -> Mapping[bytes, Tuple[str, ...]]:
2052+
) -> Mapping[bytes, tuple[str, ...]]:
20482053
"""Fetches rows from a Smalltable.
20492054
20502055
Retrieves rows pertaining to the given keys from the Table instance
@@ -2081,7 +2086,7 @@ Similarly, this variation on `Args:` with a line break is also allowed:
20812086
def fetch_smalltable_rows(table_handle: smalltable.Table,
20822087
keys: Sequence[Union[bytes, str]],
20832088
require_all_keys: bool = False,
2084-
) -> Mapping[bytes, Tuple[str, ...]]:
2089+
) -> Mapping[bytes, tuple[str, ...]]:
20852090
"""Fetches rows from a Smalltable.
20862091
20872092
Retrieves rows pertaining to the given keys from the Table instance
@@ -2215,23 +2220,20 @@ punctuation, spelling, and grammar help with that goal.
22152220
Use an
22162221
[f-string](https://docs.python.org/3/reference/lexical_analysis.html#f-strings),
22172222
the `%` operator, or the `format` method for formatting strings, even when the
2218-
parameters are all strings. Use your best judgment to decide between `+` and `%`
2219-
(or `format`) though. Do not use `%` or the `format` method for pure
2220-
concatenation.
2223+
parameters are all strings. Use your best judgment to decide between `+` and
2224+
string formatting.
22212225
22222226
```python
2223-
Yes: x = a + b
2227+
Yes: x = f'name: {name}; score: {n}'
22242228
x = '%s, %s!' % (imperative, expletive)
22252229
x = '{}, {}'.format(first, second)
22262230
x = 'name: %s; score: %d' % (name, n)
22272231
x = 'name: {}; score: {}'.format(name, n)
2228-
x = f'name: {name}; score: {n}'
2232+
x = a + b
22292233
```
22302234
22312235
```python
2232-
No: x = '%s%s' % (a, b) # use + in this case
2233-
x = '{}{}'.format(a, b) # use + in this case
2234-
x = first + ', ' + second
2236+
No: x = first + ', ' + second
22352237
x = 'name: ' + name + '; score: ' + str(n)
22362238
```
22372239
@@ -2524,7 +2526,7 @@ event ("Remove this code when all clients can handle XML responses.").
25242526
### 3.13 Imports formatting
25252527
25262528
Imports should be on separate lines; there are
2527-
[exceptions for `typing` imports](#typing-imports).
2529+
[exceptions for `typing` and `collections.abc` imports](#typing-imports).
25282530
25292531
E.g.:
25302532
@@ -2692,7 +2694,8 @@ change in complexity.
26922694
26932695
`module_name`, `package_name`, `ClassName`, `method_name`, `ExceptionName`,
26942696
`function_name`, `GLOBAL_CONSTANT_NAME`, `global_var_name`, `instance_var_name`,
2695-
`function_parameter_name`, `local_var_name`.
2697+
`function_parameter_name`, `local_var_name`, `query_proper_noun_for_thing`,
2698+
`send_acronym_via_https`.
26962699
26972700
26982701
Function names, variable names, and filenames should be descriptive; eschew
@@ -2713,6 +2716,8 @@ Always use a `.py` filename extension. Never use dashes.
27132716
- counters or iterators (e.g. `i`, `j`, `k`, `v`, et al.)
27142717
- `e` as an exception identifier in `try/except` statements.
27152718
- `f` as a file handle in `with` statements
2719+
- private [`TypeVar`s](#typing-type-var) with no constraints (e.g. `_T`,
2720+
`_U`, `_V`)
27162721
27172722
Please be mindful not to abuse single-character naming. Generally speaking,
27182723
descriptiveness should be proportional to the name's scope of visibility.
@@ -2949,6 +2954,8 @@ the function into smaller and more manageable pieces.
29492954
* In methods, only annotate `self`, or `cls` if it is necessary for proper
29502955
type information. e.g., `@classmethod def create(cls: Type[T]) -> T: return
29512956
cls()`
2957+
* Similarly, don't feel compelled to annotate the return value of `__init__`
2958+
(where `None` is the only valid option).
29522959
* If any other variable or a returned type should not be expressed, use `Any`.
29532960
* You are not required to annotate all the functions in a module.
29542961
- At least annotate your public APIs.
@@ -2993,7 +3000,7 @@ is too long, indent by 4 in a new line.
29933000
29943001
```python
29953002
def my_method(
2996-
self, first_var: int) -> Tuple[MyLongType1, MyLongType1]:
3003+
self, first_var: int) -> tuple[MyLongType1, MyLongType1]:
29973004
...
29983005
```
29993006
@@ -3005,7 +3012,7 @@ closing parenthesis with the `def`.
30053012
Yes:
30063013
def my_method(
30073014
self, other_arg: Optional[MyLongType]
3008-
) -> Dict[OtherLongType, MyLongType]:
3015+
) -> dict[OtherLongType, MyLongType]:
30093016
...
30103017
```
30113018
@@ -3017,7 +3024,7 @@ opening one, but this is less readable.
30173024
No:
30183025
def my_method(self,
30193026
other_arg: Optional[MyLongType]
3020-
) -> Dict[OtherLongType, MyLongType]:
3027+
) -> dict[OtherLongType, MyLongType]:
30213028
...
30223029
```
30233030
@@ -3027,9 +3034,9 @@ too long to be on a single line (try to keep sub-types unbroken).
30273034
```python
30283035
def my_method(
30293036
self,
3030-
first_var: Tuple[List[MyLongType1],
3031-
List[MyLongType2]],
3032-
second_var: List[Dict[
3037+
first_var: tuple[list[MyLongType1],
3038+
list[MyLongType2]],
3039+
second_var: list[dict[
30333040
MyLongType3, MyLongType4]]) -> None:
30343041
...
30353042
```
@@ -3146,7 +3153,7 @@ long:
31463153
31473154
```python
31483155
_ShortName = module_with_long_name.TypeWithLongName
3149-
ComplexMap = Mapping[str, List[Tuple[int, int]]]
3156+
ComplexMap = Mapping[str, list[tuple[int, int]]]
31503157
```
31513158
31523159
Other examples are complex nested types and multiple return variables from a
@@ -3207,9 +3214,9 @@ have a single repeated type or a set number of elements with different types.
32073214
The latter is commonly used as the return type from a function.
32083215
32093216
```python
3210-
a = [1, 2, 3] # type: List[int]
3211-
b = (1, 2, 3) # type: Tuple[int, ...]
3212-
c = (1, "2", 3.5) # type: Tuple[int, str, float]
3217+
a = [1, 2, 3] # type: list[int]
3218+
b = (1, 2, 3) # type: tuple[int, ...]
3219+
c = (1, "2", 3.5) # type: tuple[int, str, float]
32133220
```
32143221
32153222
<a id="s3.19.10-typevars"></a>
@@ -3227,10 +3234,10 @@ function `TypeVar` is a common way to use them.
32273234
Example:
32283235
32293236
```python
3230-
from typing import List, TypeVar
3231-
T = TypeVar("T")
3237+
from typing import TypeVar
3238+
_T = TypeVar("_T")
32323239
...
3233-
def next(l: List[T]) -> T:
3240+
def next(l: list[_T]) -> _T:
32343241
return l.pop()
32353242
```
32363243
@@ -3254,6 +3261,26 @@ def check_length(x: AnyStr) -> AnyStr:
32543261
raise ValueError()
32553262
```
32563263
3264+
A TypeVar must have a descriptive name, unless it meets all of the following
3265+
criteria:
3266+
3267+
* not externally visible
3268+
* not constrained
3269+
3270+
```python
3271+
Yes:
3272+
_T = TypeVar("_T")
3273+
AddableType = TypeVar("AddableType", int, float, str)
3274+
AnyFunction = TypeVar("AnyFunction", bound=Callable)
3275+
```
3276+
3277+
```python
3278+
No:
3279+
T = TypeVar("T")
3280+
_T = TypeVar("_T", int, float, str)
3281+
_F = TypeVar("_F", bound=Callable)
3282+
```
3283+
32573284
<a id="s3.19.11-string-types"></a>
32583285
<a id="s3.19.11-strings"></a>
32593286
<a id="31911-string-types"></a>
@@ -3314,19 +3341,21 @@ return type is the same as the argument type in the code above, use
33143341
<a id="typing-imports"></a>
33153342
#### 3.19.12 Imports For Typing
33163343
3317-
For classes from the `typing` module, always import the class itself. You are
3318-
explicitly allowed to import multiple specific classes on one line from the
3319-
`typing` module. Ex:
3344+
For classes from the `typing` and `collections.abc` modules for use in
3345+
annotations, always import the class itself. This keeps common annotations more
3346+
concise and matches typing practices used around the world. You are explicitly
3347+
allowed to import multiple specific classes on one line from the `typing` and
3348+
`collections.abc` modules. Ex:
33203349
33213350
```python
3322-
from typing import Any, Dict, Optional
3351+
from collections.abc import Mapping, Sequence
3352+
from typing import Any, Union
33233353
```
33243354
3325-
Given that this way of importing from `typing` adds items to the local
3326-
namespace, any names in `typing` should be treated similarly to keywords, and
3327-
not be defined in your Python code, typed or not. If there is a collision
3328-
between a type and an existing name in a module, import it using `import x as
3329-
y`.
3355+
Given that this way of importing adds items to the local namespace, names in
3356+
`typing` or `collections.abc` should be treated similarly to keywords, and not
3357+
be defined in your Python code, typed or not. If there is a collision between a
3358+
type and an existing name in a module, import it using `import x as y`.
33303359
33313360
```python
33323361
from typing import Any as AnyType
@@ -3400,12 +3429,12 @@ When annotating, prefer to specify type parameters for generic types; otherwise,
34003429
[the generics' parameters will be assumed to be `Any`](https://www.python.org/dev/peps/pep-0484/#the-any-type).
34013430
34023431
```python
3403-
def get_names(employee_ids: List[int]) -> Dict[int, Any]:
3432+
def get_names(employee_ids: list[int]) -> dict[int, Any]:
34043433
...
34053434
```
34063435
34073436
```python
3408-
# These are both interpreted as get_names(employee_ids: List[Any]) -> Dict[Any, Any]
3437+
# These are both interpreted as get_names(employee_ids: list[Any]) -> dict[Any, Any]
34093438
def get_names(employee_ids: list) -> Dict:
34103439
...
34113440
@@ -3418,13 +3447,13 @@ remember that in many cases [`TypeVar`](#typing-type-var) might be more
34183447
appropriate:
34193448
34203449
```python
3421-
def get_names(employee_ids: List[Any]) -> Dict[Any, str]:
3450+
def get_names(employee_ids: list[Any]) -> dict[Any, str]:
34223451
"""Returns a mapping from employee ID to employee name for given IDs."""
34233452
```
34243453
34253454
```python
3426-
T = TypeVar('T')
3427-
def get_names(employee_ids: List[T]) -> Dict[T, str]:
3455+
_T = TypeVar('_T')
3456+
def get_names(employee_ids: list[_T]) -> dict[_T, str]:
34283457
"""Returns a mapping from employee ID to employee name for given IDs."""
34293458
```
34303459

0 commit comments

Comments
 (0)