diff --git a/rules/S7524/metadata.json b/rules/S7524/metadata.json new file mode 100644 index 00000000000..2c63c085104 --- /dev/null +++ b/rules/S7524/metadata.json @@ -0,0 +1,2 @@ +{ +} diff --git a/rules/S7524/python/metadata.json b/rules/S7524/python/metadata.json new file mode 100644 index 00000000000..2d1053825e4 --- /dev/null +++ b/rules/S7524/python/metadata.json @@ -0,0 +1,25 @@ +{ + "title": "\"async for\" should be used for asynchronous iterators", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant/Issue", + "constantCost": "5min" + }, + "tags": [ + "async" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-7524", + "sqKey": "S7524", + "scope": "All", + "defaultQualityProfiles": ["Sonar way"], + "quickfix": "covered", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW", + "RELIABILITY": "LOW" + }, + "attribute": "CONVENTIONAL" + } +} diff --git a/rules/S7524/python/rule.adoc b/rules/S7524/python/rule.adoc new file mode 100644 index 00000000000..4c5792bad24 --- /dev/null +++ b/rules/S7524/python/rule.adoc @@ -0,0 +1,94 @@ +This rule raises an issue when a synchronous `for` loop is used with an iterator implementing the asynchronous iteration protocol. + +== Why is this an issue? + +When working inside an `async` function, you should use `async for` to iterate over asynchronous iterators. An asynchronous iterator is an object that provides `+__aiter__+` and `+__anext__+` methods and should be used instead of a synchronous `for` loop. + +=== What is the potential impact? + +Not following the proper async pattern can lead to: + +* **Incorrect Logic**: It shows a misunderstanding of the asynchronous nature of the data source. +* **Inconsistent async usage**: Mixing synchronous and asynchronous iteration patterns makes code harder to read and maintain. + +== How to fix it + +Use the `async for` statement to iterate over asynchronous iterators inside an `async` function. + +=== Code examples + +==== Noncompliant code example + +[source,python,diff-id=1,diff-type=noncompliant] +---- +class MyIterator: + def __init__(self): + ... + + def __iter__(self): + ... + + def __next__(self): + ... + + def __aiter__(self): + ... + + async def __anext__(self): + ... + +async def main(): + for _ in MyIterator(): # Noncompliant + ... +---- + +==== Compliant solution + +[source,python,diff-id=1,diff-type=compliant] +---- +class MyIterator: + def __init__(self): + ... + + def __iter__(self): + ... + + def __next__(self): + ... + + def __aiter__(self): + ... + + async def __anext__(self): + ... + +async def main(): + async for _ in MyIterator(): # Compliant + ... +---- + +ifdef::env-github,rspecator-view[] + +== Implementation Specification +(visible only on this page) + +=== Message + +Use "async for" for asynchronous iterators + +Consider implementing quickfix to replace the `for` keyword with `async for`. + +=== Highlighting + +* Primary location: The `for` keyword of the loop. +* Secondary locations: The `async` keyword of the enclosing function +* Secondary locations: The `+__aiter__+` and `+__anext__+` methods if we can get the location +endif::env-github,rspecator-view[] + +== Resources + +=== Documentation + +* Python Documentation - https://docs.python.org/3/reference/compound_stmts.html#the-async-for-statement[The async for statement] +* Python Documentation - https://docs.python.org/3/reference/datamodel.html#the-iterator-protocol[The iterator protocol] +* Python Documentation - https://docs.python.org/3/reference/datamodel.html#asynchronous-iterators[Asynchronous Iterators]