Skip to content

Commit 1bf6106

Browse files
RomanValovjakkdl
andauthored
async102 not applicable to asyncio (#393)
* async102 not applicable to asyncio * update tests to reflect async102 changes * Apply suggestions from code review minor fixes --------- Co-authored-by: John Litborn <[email protected]>
1 parent 4af99c4 commit 1bf6106

File tree

12 files changed

+57
-127
lines changed

12 files changed

+57
-127
lines changed

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Changelog
44

55
`CalVer, YY.month.patch <https://calver.org/>`_
66

7+
25.7.1
8+
======
9+
- :ref:`ASYNC102 <async102>` no longer triggered for asyncio due to different cancellation semantics it uses.
10+
711
25.5.3
812
======
913
- :ref:`ASYNC115 <async115>` and :ref:`ASYNC116 <async116>` now also checks kwargs.

docs/rules.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ _`ASYNC101` : yield-in-cancel-scope
2424
_`ASYNC102` : await-in-finally-or-cancelled
2525
``await`` inside ``finally``, :ref:`cancelled-catching <cancelled>` ``except:``, or ``__aexit__`` must have shielded :ref:`cancel scope <cancel_scope>` with timeout.
2626
If not, the async call will immediately raise a new cancellation, suppressing any cancellation that was caught.
27+
Not applicable to asyncio due to edge-based cancellation semantics it uses as opposed to level-based used by trio and anyio.
2728
See :ref:`ASYNC120 <async120>` for the general case where other exceptions might get suppressed.
28-
This is currently not able to detect asyncio shields.
2929

3030
ASYNC103 : no-reraise-cancelled
3131
:ref:`cancelled`-catching exception that does not reraise the exception.

docs/usage.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ adding the following to your ``.pre-commit-config.yaml``:
3333
minimum_pre_commit_version: '2.9.0'
3434
repos:
3535
- repo: https://github.com/python-trio/flake8-async
36-
rev: 25.5.3
36+
rev: 25.7.1
3737
hooks:
3838
- id: flake8-async
3939
# args: ["--enable=ASYNC100,ASYNC112", "--disable=", "--autofix=ASYNC"]

flake8_async/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939

4040
# CalVer: YY.month.patch, e.g. first release of July 2022 == "22.7.1"
41-
__version__ = "25.5.3"
41+
__version__ = "25.7.1"
4242

4343

4444
# taken from https://github.com/Zac-HD/shed

flake8_async/visitors/visitor102_120.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ def async_call_checker(
7474
# non-critical exception handlers have the statement name set to "except"
7575
if self._critical_scope.name == "except":
7676
self._potential_120.append((node, self._critical_scope))
77-
else:
77+
# not applicable to asyncio due to different cancellation semantics it uses
78+
elif self.library != ("asyncio",):
7879
self.error(node, self._critical_scope, error_code="ASYNC102")
7980

8081
def visit_Raise(self, node: ast.Raise):
@@ -84,10 +85,7 @@ def visit_Raise(self, node: ast.Raise):
8485

8586
def is_safe_aclose_call(self, node: ast.Await) -> bool:
8687
return (
87-
# don't mark calls safe in asyncio-only files
88-
# a more defensive option would be `asyncio not in self.library`
89-
self.library != ("asyncio",)
90-
and isinstance(node.value, ast.Call)
88+
isinstance(node.value, ast.Call)
9189
# only known safe if no arguments
9290
and not node.value.args
9391
and not node.value.keywords

tests/eval_files/async102.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# type: ignore
22
# ARG --enable=ASYNC102,ASYNC120
3-
# NOASYNCIO # TODO: support asyncio shields
3+
# ASYNCIO_NO_ERROR # ASYNC102 not applicable to asyncio
44
from contextlib import asynccontextmanager
55

66
import trio
@@ -310,3 +310,38 @@ async def foo_nested_cs():
310310
# treat __aexit__ as a critical scope
311311
async def __aexit__():
312312
await foo() # error: 4, Statement("__aexit__", lineno-1)
313+
314+
315+
# exclude `finally: await x.aclose()` with no arguments
316+
async def foo_aclose_noargs():
317+
# no type tracking in this check, we allow any call that looks like
318+
# `await [...].aclose()`
319+
x = None
320+
321+
try:
322+
...
323+
except BaseException:
324+
await x.aclose()
325+
await x.y.aclose()
326+
finally:
327+
await x.aclose()
328+
await x.y.aclose()
329+
330+
331+
# should still raise errors if there's args, as that indicates it's a non-standard aclose
332+
async def foo():
333+
# no type tracking in this check
334+
x = None
335+
336+
try:
337+
...
338+
except BaseException:
339+
await x.aclose(foo) # ASYNC102: 8, Statement("BaseException", lineno-1)
340+
await x.aclose(bar=foo) # ASYNC102: 8, Statement("BaseException", lineno-2)
341+
await x.aclose(*foo) # ASYNC102: 8, Statement("BaseException", lineno-3)
342+
await x.aclose(None) # ASYNC102: 8, Statement("BaseException", lineno-4)
343+
finally:
344+
await x.aclose(foo) # ASYNC102: 8, Statement("try/finally", lineno-8)
345+
await x.aclose(bar=foo) # ASYNC102: 8, Statement("try/finally", lineno-9)
346+
await x.aclose(*foo) # ASYNC102: 8, Statement("try/finally", lineno-10)
347+
await x.aclose(None) # ASYNC102: 8, Statement("try/finally", lineno-11)

tests/eval_files/async102_aclose.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

tests/eval_files/async102_aclose_args.py

Lines changed: 0 additions & 24 deletions
This file was deleted.

tests/eval_files/async102_anyio.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# type: ignore
2+
# this test will raise the same errors with trio, despite trio.get_cancelled_exc_class not existing
3+
# marked not to run the tests though as error messages will only refer to anyio
24
# NOTRIO
3-
# NOASYNCIO
5+
# ASYNCIO_NO_ERROR # ASYNC102 not applicable to asyncio
46
# BASE_LIBRARY anyio
5-
# this test will raise the same errors with trio/asyncio, despite [trio|asyncio].get_cancelled_exc_class not existing
6-
# marked not to run the tests though as error messages will only refer to anyio
77
import anyio
88
from anyio import get_cancelled_exc_class
99

tests/eval_files/async102_asyncio.py

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)