return-in-generator (B901)
Derived from the flake8-bugbear linter.
This rule is unstable and in preview. The --preview
flag is required for use.
What it does
Checks for return {value}
statements in functions that also contain yield
or yield from
statements.
Why is this bad?
Using return {value}
in a generator function was syntactically invalid in
Python 2. In Python 3 return {value}
can be used in a generator; however,
the combination of yield
and return
can lead to confusing behavior, as
the return
statement will cause the generator to raise StopIteration
with the value provided, rather than returning the value to the caller.
For example, given:
from collections.abc import Iterable
from pathlib import Path
def get_file_paths(file_types: Iterable[str] | None = None) -> Iterable[Path]:
dir_path = Path(".")
if file_types is None:
return dir_path.glob("*")
for file_type in file_types:
yield from dir_path.glob(f"*.{file_type}")
Readers might assume that get_file_paths()
would return an iterable of
Path
objects in the directory; in reality, though, list(get_file_paths())
evaluates to []
, since the return
statement causes the generator to raise
StopIteration
with the value dir_path.glob("*")
:
>>> list(get_file_paths(file_types=["cfg", "toml"]))
[PosixPath('setup.cfg'), PosixPath('pyproject.toml')]
>>> list(get_file_paths())
[]
For intentional uses of return
in a generator, consider suppressing this
diagnostic.
Example
from collections.abc import Iterable
from pathlib import Path
def get_file_paths(file_types: Iterable[str] | None = None) -> Iterable[Path]:
dir_path = Path(".")
if file_types is None:
return dir_path.glob("*")
for file_type in file_types:
yield from dir_path.glob(f"*.{file_type}")
Use instead: