Repository: wemake-services/wemake-python-styleguide

Issue:
False positive implicit yield violation
# Bug report

<!--
Hi, thanks for submitting a bug. We appreciate that.

But, we will need some information about what's wrong to help you.
-->

## What's wrong
With common function to split iterable by chunks WPS raises `ImplicitYieldFromViolation`
<!-- Describe what is not working. Please, attach a traceback. -->
```
def chunks(value, chunk_size):
    """Yield successive n-sized chunks from l."""
    for index in range(0, len(value), chunk_size):
        yield value[index:index + chunk_size]
```

## How is that should be
`ImplicitYieldFromViolation` should be raised only if no other operations with iterable item.
<!-- Describe how it should work. -->

<!--

  Love wemake-python-styleguide? ❤️
  Do you want your bugs to be solved faster?

  Please consider supporting our collective:
👉  https://opencollective.com/wemake-python-styleguide/donate

-->



Discussion:
What about 

```python
yield from (
    value[index:index + chunk_size]
    for index in range(0, len(value), chunk_size)
)
```

?
Less readable in my taste. But if no other choices I will use it. Thanks.
Thanks for raising this, I guess we need to update the violation docs about this case.
@sobolevn there is still a bug. 
`Python does not support 'yield from' inside async functions`
Yes, that's true.
This rule should be ignored for `async` functions.

Can you please add a check that `if isinstance(get_context(node), ast.AsyncFunctionDef): return`?
Of course, I'll do it.

Resolving patch:
diff --git a/wemake_python_styleguide/visitors/ast/loops.py b/wemake_python_styleguide/visitors/ast/loops.py
--- a/wemake_python_styleguide/visitors/ast/loops.py
+++ b/wemake_python_styleguide/visitors/ast/loops.py
@@ -277,6 +277,9 @@ def _check_implicit_sum(self, node: AnyFor) -> None:
             self.add_violation(ImplicitSumViolation(node))
 
     def _check_implicit_yield_from(self, node: AnyFor) -> None:
+        if isinstance(nodes.get_context(node), ast.AsyncFunctionDef):
+            # Python does not support 'yield from' inside async functions
+            return
         is_implicit_yield_from = (
             len(node.body) == 1 and
             isinstance(node.body[0], ast.Expr) and


Test patch:
diff --git a/tests/test_visitors/test_ast/test_loops/test_loops/test_implicit_yield_from.py b/tests/test_visitors/test_ast/test_loops/test_loops/test_implicit_yield_from.py
--- a/tests/test_visitors/test_ast/test_loops/test_loops/test_implicit_yield_from.py
+++ b/tests/test_visitors/test_ast/test_loops/test_loops/test_implicit_yield_from.py
@@ -31,10 +31,10 @@ def test_implicit_yield_from(
     code,
     template,
     default_options,
-    mode,
+    regular_wrapper,
 ):
     """Ensures that implicit ``yield from`` are not allowed."""
-    tree = parse_ast_tree(mode(template.format(code)))
+    tree = parse_ast_tree(regular_wrapper(template.format(code)))
 
     visitor = WrongLoopDefinitionVisitor(default_options, tree=tree)
     visitor.run()
@@ -42,6 +42,32 @@ def test_implicit_yield_from(
     assert_errors(visitor, [ImplicitYieldFromViolation])
 
 
+@pytest.mark.parametrize('template', [
+    for_loop_template,
+])
+@pytest.mark.parametrize('code', [
+    'yield',
+    'yield None',
+    'yield index',
+    'yield 10',
+])
+def test_async_implicit_yield_from(
+    assert_errors,
+    parse_ast_tree,
+    code,
+    template,
+    default_options,
+    async_wrapper,
+):
+    """Ensures that implicit ``yield from`` in async functions are allowed."""
+    tree = parse_ast_tree(async_wrapper(template.format(code)))
+
+    visitor = WrongLoopDefinitionVisitor(default_options, tree=tree)
+    visitor.run()
+
+    assert_errors(visitor, [])
+
+
 @pytest.mark.parametrize('template', [
     for_loop_template,
 ])