org.sonar.l10n.py.rules.python.S3862.html Maven / Gradle / Ivy
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, yield
from
and iterable unpacking only work with iterable objects. In order
to be iterable, an object should have either an __iter__
method or a __getitem__
method implementing the 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__
:
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__
:
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:
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 asynchronous iterable, i.e. an
object having the __aiter__
method, requires the use of 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
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:
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
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.
async def async_function():
# async generators
async def async_generator():
yield 1
async for a in async_generator():
print(a)
Resources
Documentation
Standards