Skip to content

Commit 4fa719f

Browse files
authored
async101 & async119 now respects transform-async-generator-decorators (#316)
* async101 & async119 now respects transform-async-generator-decorators
1 parent 9ce7039 commit 4fa719f

9 files changed

Lines changed: 74 additions & 5 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
runs-on: ubuntu-latest
3131
strategy:
3232
matrix:
33-
python-version: ['3.9', '3.10', '3.11', '3.12', 3.13-dev]
33+
python-version: ['3.9', '3.10', '3.11', '3.12', 3.13]
3434
fail-fast: false
3535
steps:
3636
- uses: actions/checkout@v4

docs/changelog.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Changelog
66

77
24.10.2
88
=======
9+
- :ref:`ASYNC101 <async101>` and :ref:`ASYNC119 <async119>` are now silenced for decorators in :ref:`transform-async-generator-decorators`
910
- :ref:`ASYNC102 <async102>` now also warns about ``await()`` inside ``__aexit__``.
1011

1112
24.10.1
@@ -39,7 +40,7 @@ Changelog
3940

4041
24.8.1
4142
======
42-
- Add config option ``transform-async-generator-decorators``, to list decorators which
43+
- Add config option :ref:`transform-async-generator-decorators`, to list decorators which
4344
suppress :ref:`ASYNC900 <async900>`.
4445

4546
24.6.1

docs/rules.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ _`ASYNC100` : cancel-scope-no-checkpoint
1515
This check also treats ``yield`` as a checkpoint, since checkpoints can happen in the caller we yield to.
1616
See :ref:`ASYNC912 <async912>` which will in addition guarantee checkpoints on every code path.
1717

18-
ASYNC101 : yield-in-cancel-scope
18+
_`ASYNC101` : yield-in-cancel-scope
1919
``yield`` inside a :ref:`taskgroup_nursery` or :ref:`timeout_context` is only safe when implementing a context manager - otherwise, it breaks exception handling.
2020
See `this thread <https://discuss.python.org/t/preventing-yield-inside-certain-context-managers/1091/23>`_ for discussion of a future PEP.
2121
This has substantial overlap with :ref:`ASYNC119 <ASYNC119>`, which will warn on almost all instances of ASYNC101, but ASYNC101 is about a conceptually different problem that will not get resolved by `PEP 533 <https://peps.python.org/pep-0533/>`_.

docs/usage.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,21 @@ Specified patterns must not have parentheses, and will only match when the patte
318318
def my_blocking_call(): # it's also safe to use the name in other contexts
319319
...
320320
arbitrary_other_function(my_blocking_call=None)
321+
322+
.. _transform-async-generator-decorators:
323+
324+
``transform-async-generator-decorators``
325+
----------------------------------------
326+
Comma-separated list of decorators that make async generators safe, disabling
327+
:ref:`ASYNC900 <ASYNC900>`, :ref:`ASYNC101 <ASYNC101>`, and :ref:`ASYNC119 <ASYNC119>` warnings for functions decorated with any of them.
328+
``[pytest.]fixture`` and ``[contextlib.]asynccontextmanager`` are always considered safe.
329+
Decorators can be dotted or not, as well as support * as a wildcard.
330+
331+
Example
332+
^^^^^^^
333+
334+
.. code-block:: none
335+
336+
transform-async-generator-decorators =
337+
fastapi.Depends
338+
trio_util.trio_async_generator

flake8_async/visitors/visitor101.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ def visit_FunctionDef(self, node: cst.FunctionDef):
7070
self.save_state(node, "_yield_is_error", "_safe_decorator")
7171
self._yield_is_error = False
7272
self._safe_decorator = func_has_decorator(
73-
node, "contextmanager", "asynccontextmanager", "fixture"
73+
node,
74+
"contextmanager",
75+
"asynccontextmanager",
76+
"fixture",
77+
*self.options.transform_async_generator_decorators,
7478
)
7579

7680
# trigger on leaving yield so any comments are parsed for noqas

flake8_async/visitors/visitors.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,10 @@ def visit_AsyncFunctionDef(
334334
self.save_state(node, "unsafe_function", "contextmanager")
335335
self.contextmanager = False
336336
if isinstance(node, ast.AsyncFunctionDef) and not has_decorator(
337-
node, "asynccontextmanager"
337+
node,
338+
"asynccontextmanager",
339+
"fixture",
340+
*self.options.transform_async_generator_decorators,
338341
):
339342
self.unsafe_function = True
340343
else:

tests/eval_files/async101.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# ASYNCIO_NO_ERROR
2+
# ARG --no-checkpoint-warning-decorator=no_checkpoint_warning_decorator
3+
# ARG --transform-async-generator-decorators=transform_async_gen_decorator
24

35
# This file contains errors shared between trio and anyio, since they have some
46
# overlap in naming.
@@ -127,3 +129,23 @@ def foo_pytest_fixture_paren():
127129
def foo_pytest_fixture_params():
128130
with trio.CancelScope() as _:
129131
yield 1
132+
133+
134+
def no_checkpoint_warning_decorator(_: object): ...
135+
136+
137+
def transform_async_gen_decorator(_: object): ...
138+
139+
140+
# --no-checkpoint-warning-decorator does not mark as safe
141+
@no_checkpoint_warning_decorator
142+
def no_checkpoint_warning_deco_fun():
143+
with trio.CancelScope():
144+
yield 1 # error: 8
145+
146+
147+
# --transform-async-generator-decorators marks as safe
148+
@transform_async_gen_decorator
149+
def transfor_async_gen_deco_fun():
150+
with trio.CancelScope():
151+
yield 1 # safe

tests/eval_files/async119.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# ARG --no-checkpoint-warning-decorator=no_checkpoint_warning_decorator
2+
# ARG --transform-async-generator-decorators=transform_async_gen_decorator
13
import contextlib
24

35
from contextlib import asynccontextmanager
@@ -62,3 +64,21 @@ async def safe_in_contextmanager():
6264
async def safe_in_contextmanager2():
6365
with open(""):
6466
yield
67+
68+
69+
def no_checkpoint_warning_decorator(_: object): ...
70+
71+
72+
def transform_async_gen_decorator(_: object): ...
73+
74+
75+
@no_checkpoint_warning_decorator
76+
async def no_checkpoint_warning_deco_fun():
77+
with open(""):
78+
yield # error: 8
79+
80+
81+
@transform_async_gen_decorator
82+
async def transfor_async_gen_deco_fun():
83+
with open(""):
84+
yield # safe

tests/eval_files/async900.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# type: ignore
22
# ARG --no-checkpoint-warning-decorator=asynccontextmanager,other_context_manager
3+
# transform-async-generator-decorators set further down
34
from contextlib import asynccontextmanager
45

56

0 commit comments

Comments
 (0)