This rule raises an issue when a non iterable object is used in a `for-in` loop, in a `yield from` or when it is unpacked. == Why is this an issue? `for-in` loops, https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator[`yield from`] and iterable unpacking only work with https://docs.python.org/3/glossary.html#term-iterable[iterable objects]. In order to be iterable, an object should have either an ``++__iter__++`` method or a ``++__getitem__++`` method implementing the https://docs.python.org/3/glossary.html#term-sequence[Sequence] protocol. When trying to iterate over an object which does not implement the required methods, a `TypeError` will be raised. Below is an example of a basic implementation of a iterator with ``++__iter__++``: [source,python] ---- class IterExample(object): def __init__(self): self._values = [1,2,3,4] def __iter__(self): return iter(self._values) ---- Here is a similar example with ``++__getitem__++``: [source,python] ---- class GetItemExample(object): def __init__(self): self._values = [1,2,3,4] def __getitem__(self, item): return self._values[item] ---- These implementations make it possible to execute the following program: [source,python] ---- my_iterator = IterExample() for i in my_iterator: print(i) # Prints 1,2,3,4 my_iterator = GetItemExample() for i in my_iterator: print(i) # Prints 1,2,3,4 ---- Note also that iterating over an https://docs.python.org/3/glossary.html#term-asynchronous-iterable[asynchronous iterable], i.e. an object having the ``++__aiter__++`` method, requires the use of https://docs.python.org/3/reference/compound_stmts.html#the-async-for-statement[``++async for ... in++``] instead of ``++for ... in++``. Failing to provide the `async` keyword will result in a `TypeError` stating the object is not iterable. == How to fix it Make sure your object is an iterable when using it in `for-in` loops,`yield from` and unpacking statements, by implementing ``++__iter__++`` or ``++__getitem__++``. To iterate over an asynchronous iterable, make sure to use the `async` keyword, i.e `async for ... in`. === Code examples ==== Noncompliant code example [source,python,diff-id=1,diff-type=noncompliant] ---- class MyIterable: def __init__(self, values): self._values = values my_iterable = MyIterable(range(10)) for a in my_iterable: # Noncompliant: MyIterable is not an iterable print(a) a, b, *c = my_iterable # Noncompliant: MyIterable is not an iterable # yield from def generator(): yield from my_iterable # Noncompliant: MyIterable is not an iterable ---- For async generators: [source,python,diff-id=2,diff-type=noncompliant] ---- async def async_function(): # async generators async def async_generator(): yield 1 for a in async_generator(): # Noncompliant: "async" is missing before "for" print(a) ---- ==== Compliant solution [source,python,diff-id=1,diff-type=compliant] ---- class MyIterable: def __init__(self, values): self._values = values def __iter__(self): return iter(self._values) my_iterable = MyIterable(range(10)) for a in my_iterable: print(a) a, b, *c = my_iterable # yield from def generator(): yield from my_iterable ---- Make sure to use the `async` keyword when iterating over async generators. [source,python,diff-id=2,diff-type=compliant] ---- async def async_function(): # async generators async def async_generator(): yield 1 async for a in async_generator(): print(a) ---- == Resources === Documentation * https://docs.python.org/3/library/stdtypes.html#iterator-types[Iterator Types] === Standards * https://www.python.org/dev/peps/pep-0234/#python-api-specification[PEP 234 - Iterators] ifdef::env-github,rspecator-view[] ''' == Implementation Specification (visible only on this page) === Message * Replace this expression with an iterable object. * Add "async" before "for"; Expression is an async generator. === Highlighting * Primary: the non-iterable expression * Secondary: The class/function/... definition message: 'Definition of "X".' endif::env-github,rspecator-view[]