diff --git a/pyguide.md b/pyguide.md index 15bfed3..71eb6da 100644 --- a/pyguide.md +++ b/pyguide.md @@ -212,12 +212,7 @@ that the arguments are actually unused. ### 2.2 Imports Use `import` statements for packages and modules only, not for individual -classes or functions. Classes imported from the -[`typing` module](#typing-imports), [`collections.abc` module](#typing-imports), -[`typing_extensions` module](https://github.com/python/typing/tree/master/typing_extensions), -and redirects from the -[six.moves module](https://six.readthedocs.io/#module-six.moves) -are exempt from this rule. +classes or functions. @@ -272,6 +267,18 @@ Do not use relative names in imports. Even if the module is in the same package, use the full package name. This helps prevent unintentionally importing a package twice. + +##### 2.2.4.1 Exemptions + +Exemptions from this rule: + +* Classes imported from the [`typing` module](#typing-imports). +* Classes imported from the [`collections.abc` module](#typing-imports). +* Classes imported from the + [`typing_extensions` module](https://github.com/python/typing/tree/HEAD/typing_extensions). +* Redirects from the + [six.moves module](https://six.readthedocs.io/#module-six.moves). + @@ -722,14 +729,12 @@ Yes: for key in adict: ... if obj in alist: ... for line in afile: ... for k, v in adict.items(): ... - for k, v in six.iteritems(adict): ... ``` ```python No: for key in adict.keys(): ... if not adict.has_key(key): ... for line in afile.readlines(): ... - for k, v in dict.iteritems(): ... ``` @@ -744,7 +749,7 @@ Use generators as needed. -#### 2.9 Definition +#### 2.9.1 Definition A generator function returns an iterator that yields a value each time it executes a yield statement. After it yields a value, the runtime state of the @@ -766,7 +771,8 @@ creates an entire list of values at once. #### 2.9.3 Cons -None. +Local variables in the generator will not be garbage collected until the +generator is either consumed to exhaustion or itself garbage collected. @@ -777,6 +783,11 @@ None. Fine. Use "Yields:" rather than "Returns:" in the docstring for generator functions. +If the generator manages an expensive resource, make sure to force the clean up. + +A good way to do the clean up is by wrapping the generator with a context +manager [PEP-0533](https://peps.python.org/pep-0533/). + @@ -1415,14 +1426,6 @@ In code that may execute on versions as old as 3.5 rather than >= 3.7, import: from __future__ import generator_stop ``` -For legacy code with the burden of continuing to support 2.7, import: - -```python -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -``` - For more information read the [Python future statement definitions](https://docs.python.org/3/library/__future__.html) documentation. @@ -1433,19 +1436,7 @@ feature a specific future import enables in your code today, keeping it in place in the file prevents later modifications of the code from inadvertently depending on the older behavior. -Use other `from __future__` import statements as you see fit. We did not include -`unicode_literals` in our recommendations for 2.7 as it was not a clear win due -to implicit default codec conversion consequences it introduced in many places -within 2.7. Most dual-version 2-and-3 code was better off with explicit use of -`b''` and `u''` bytes and unicode string literals where necessary. - -##### The six, future, and past libraries - -When your project still needs to support use under both Python 2 and 3, use the -[six](https://pypi.org/project/six/), -[future](https://pypi.org/project/future/), and -[past](https://pypi.org/project/past/) libraries as you see fit. They exist to -make your code cleaner and life easier. +Use other `from __future__` import statements as you see fit. @@ -1455,11 +1446,10 @@ make your code cleaner and life easier. ### 2.21 Type Annotated Code -You can annotate Python 3 code with type hints according to +You can annotate Python code with type hints according to [PEP-484](https://www.python.org/dev/peps/pep-0484/), and type-check the code at build time with a type checking tool like [pytype](https://github.com/google/pytype). - Type annotations can be in the source or in a [stub pyi file](https://www.python.org/dev/peps/pep-0484/#stub-files). Whenever possible, annotations should be in the source. Use pyi files for third-party or @@ -2129,8 +2119,8 @@ If your class has public attributes, they should be documented here in an class SampleClass: """Summary of class here. - Longer class information.... - Longer class information.... + Longer class information... + Longer class information... Attributes: likes_spam: A boolean indicating if we like SPAM or not. @@ -2146,6 +2136,34 @@ class SampleClass: """Performs operation blah.""" ``` +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. + +```python +class CheeseShopAddress: + """The address of a cheese shop. + + ... + """ + +class OutOfCheeseError(Exception): + """No more cheese is available.""" +``` + +```python +class CheeseShopAddress: + """Class that describes the address of a cheese shop. + + ... + """ + +class OutOfCheeseError(Exception): + """Raised when no more cheese is available.""" +``` + @@ -2542,9 +2560,7 @@ grouped from most generic to least generic: 1. Python future import statements. For example: ```python - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function + from __future__ import annotations ``` See [above](#from-future-imports) for more information about those. @@ -2945,13 +2961,23 @@ the function into smaller and more manageable pieces. * Familiarize yourself with [PEP-484](https://www.python.org/dev/peps/pep-0484/). + * In methods, only annotate `self`, or `cls` if it is necessary for proper - type information. e.g., `@classmethod def create(cls: Type[T]) -> T: return - cls()` + type information. e.g., + + ```python + @classmethod + def create(cls: Type[T]) -> T: + return cls() + ``` + * Similarly, don't feel compelled to annotate the return value of `__init__` (where `None` is the only valid option). + * If any other variable or a returned type should not be expressed, use `Any`. + * You are not required to annotate all the functions in a module. + - At least annotate your public APIs. - Use judgment to get to a good balance between safety and clarity on the one hand, and flexibility on the other. @@ -3245,8 +3271,7 @@ def add(a: AddableType, b: AddableType) -> AddableType: ``` A common predefined type variable in the `typing` module is `AnyStr`. Use it for -multiple annotations that can be `bytes` or `unicode` and must all be the same -type. +multiple annotations that can be `bytes` or `str` and must all be the same type. ```python from typing import AnyStr @@ -3283,46 +3308,16 @@ No: #### 3.19.11 String types -The proper type for annotating strings depends on what versions of Python the -code is intended for. +> Do not use `typing.Text` in new code. It's only for Python 2/3 compatibility. -Prefer to use `str`, though `Text` is also acceptable. Be consistent in using -one or the other. For code that deals with binary data, use `bytes`. For Python -2 compatible code that processes text data (`str` or `unicode` in Python 2, -`str` in Python 3), use `Text`. +Use `str` for string/text data. For code that deals with binary data, use +`bytes`. ```python -def deals_with_text_data_in_py3(x: str) -> str: +def deals_with_text_data(x: str) -> str: ... def deals_with_binary_data(x: bytes) -> bytes: ... -def py2_compatible_text_data_processor(x: Text) -> Text: - ... -``` - -In some uncommon Python 2 compatibility cases, `str` may make sense instead of -`Text`, typically to aid compatibility when the return types aren't the same -between Python 2 and Python 3. Never use `unicode` as it doesn't exist in Python -3. The reason this discrepancy exists is because `str` means something different -in Python 2 than in Python 3. - -No: - -```python -def py2_code(x: str) -> unicode: - ... -``` - -If the type can be either bytes or text, use `Union`, with the appropriate text -type. - -```python -from typing import Text, Union -... -def py3_only(x: Union[bytes, str]) -> Union[bytes, str]: - ... -def py2_compatible(x: Union[bytes, Text]) -> Union[bytes, Text]: - ... ``` If all the string types of a function are always the same, for example if the @@ -3336,11 +3331,11 @@ return type is the same as the argument type in the code above, use #### 3.19.12 Imports For Typing -For classes from the `typing` and `collections.abc` modules for use in -annotations, always import the class itself. This keeps common annotations more -concise and matches typing practices used around the world. You are explicitly -allowed to import multiple specific classes on one line from the `typing` and -`collections.abc` modules. Ex: +For symbols from the `typing` and `collections.abc` modules used to support +static analysis and type checking, always import the symbol itself. This keeps +common annotations more concise and matches typing practices used around the +world. You are explicitly allowed to import multiple specific classes on one +line from the `typing` and `collections.abc` modules. Ex: ```python from collections.abc import Mapping, Sequence