152 lines
4.1 KiB
Plaintext
152 lines
4.1 KiB
Plaintext
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[]
|