Skip to content

Commit

Permalink
Fix false positive in use-yield-from when using yield return (#9700
Browse files Browse the repository at this point in the history
…) (#9701)

If the return value from `yield` is inspected inline, such as by
(augmented) assignment, changing the looped `yield` to `yield from` is
very likely to change the semantics of the generator, since there is an
implicit use of `generator.send`.

Closes #9696

(cherry picked from commit ea73bae)

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
  • Loading branch information
github-actions[bot] and jakelishman authored Jun 6, 2024
1 parent 192727b commit 8aba7d1
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 6 deletions.
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/9696.false_positive
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a false positive for ``use-yield-from`` when using the return value from the ``yield`` atom.

Closes #9696
15 changes: 9 additions & 6 deletions pylint/checkers/refactoring/refactoring_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,21 +1169,24 @@ def visit_yield(self, node: nodes.Yield) -> None:
if not isinstance(node.value, nodes.Name):
return

parent = node.parent.parent
loop_node = node.parent.parent
if (
not isinstance(parent, nodes.For)
or isinstance(parent, nodes.AsyncFor)
or len(parent.body) != 1
not isinstance(loop_node, nodes.For)
or isinstance(loop_node, nodes.AsyncFor)
or len(loop_node.body) != 1
# Avoid a false positive if the return value from `yield` is used,
# (such as via Assign, AugAssign, etc).
or not isinstance(node.parent, nodes.Expr)
):
return

if parent.target.name != node.value.name:
if loop_node.target.name != node.value.name:
return

if isinstance(node.frame(), nodes.AsyncFunctionDef):
return

self.add_message("use-yield-from", node=parent, confidence=HIGH)
self.add_message("use-yield-from", node=loop_node, confidence=HIGH)

@staticmethod
def _has_exit_in_scope(scope: nodes.LocalsDictNodeNG) -> bool:
Expand Down
10 changes: 10 additions & 0 deletions tests/functional/u/use/use_yield_from.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,13 @@ async def async_for_yield(agen):
async def async_yield(agen):
for item in agen:
yield item


# If the return from `yield` is used inline, don't suggest delegation.

def yield_use_send():
for item in (1, 2, 3):
_ = yield item
total = 0
for item in (1, 2, 3):
total += yield item

0 comments on commit 8aba7d1

Please # to comment.