Skip to content

Commit cd58529

Browse files
committed
Add config option for async900 decorators
1 parent bae7ab7 commit cd58529

File tree

7 files changed

+43
-5
lines changed

7 files changed

+43
-5
lines changed

docs/changelog.rst

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

55
*[CalVer, YY.month.patch](https://calver.org/)*
66

7+
24.8.1
8+
======
9+
- Add config option ``transform-async-generator-decorators``, to list decorators which
10+
suppress :ref:`ASYNC900 <async900>`.
11+
712
24.6.1
813
======
914
- Add :ref:`ASYNC120 <async120>` await-in-except.

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"python": ("https://docs.python.org/3", None),
3535
"anyio": ("https://anyio.readthedocs.io/en/latest/", None),
3636
"trio": ("https://trio.readthedocs.io/en/latest/", None),
37+
"trio-util": ("https://trio-util.readthedocs.io/en/latest/", None),
3738
# httpx? Did not seem to work on first try - I think they might not be using
3839
# sphinx at all, so probably can't link through intersphinx?
3940
# see https://github.com/encode/httpx/discussions/1220

docs/rules.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,14 @@ Optional rules disabled by default
161161
Our 9xx rules check for semantics issues, like 1xx rules, but are disabled by default due
162162
to the higher volume of warnings. We encourage you to enable them - without guaranteed
163163
:ref:`checkpoint`\ s timeouts and cancellation can be arbitrarily delayed, and async
164-
generators are prone to the problems described in :pep:`533`.
164+
generators are prone to the problems described in :pep:`789` and :pep:`533`.
165165

166166
_`ASYNC900` : unsafe-async-generator
167167
Async generator without :func:`@asynccontextmanager <contextlib.asynccontextmanager>` not allowed.
168168
You might want to enable this on a codebase since async generators are inherently unsafe and cleanup logic might not be performed.
169-
See `#211 <https://github.com/python-trio/flake8-async/issues/211>`__ and https://discuss.python.org/t/using-exceptiongroup-at-anthropic-experience-report/20888/6 for discussion.
169+
See :pep:`789` for control-flow problems, :pep:`533` for delayed cleanup problems.
170+
Further decorators can be registered with the ``--transform-async-generator-decorators``
171+
config option, e.g. :func:`trio_util.trio_async_generator`.
170172

171173
_`ASYNC910` : async-function-no-checkpoint
172174
Exit or ``return`` from async function with no guaranteed :ref:`checkpoint` or exception since function definition.

flake8_async/__init__.py

Lines changed: 14 additions & 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__ = "24.6.1"
41+
__version__ = "24.8.1"
4242

4343

4444
# taken from https://github.com/Zac-HD/shed
@@ -261,6 +261,18 @@ def add_options(option_manager: OptionManager | ArgumentParser):
261261
"mydecorator,mypackage.mydecorators.*``"
262262
),
263263
)
264+
add_argument(
265+
"--transform-async-generator-decorators",
266+
default="",
267+
required=False,
268+
type=comma_separated_list,
269+
help=(
270+
"Comma-separated list of decorators to disable ASYNC900 warnings for. "
271+
"Decorators can be dotted or not, as well as support * as a wildcard. "
272+
"For example, ``--transform-async-generator-decorators=fastapi.Depends,"
273+
"trio_util.trio_async_generator``"
274+
),
275+
)
264276
add_argument(
265277
"--exception-suppress-context-managers",
266278
default="",
@@ -391,6 +403,7 @@ def get_matching_codes(
391403
autofix_codes=autofix_codes,
392404
error_on_autofix=options.error_on_autofix,
393405
no_checkpoint_warning_decorators=options.no_checkpoint_warning_decorators,
406+
transform_async_generator_decorators=options.transform_async_generator_decorators,
394407
exception_suppress_context_managers=options.exception_suppress_context_managers,
395408
startable_in_context_manager=options.startable_in_context_manager,
396409
async200_blocking_calls=options.async200_blocking_calls,

flake8_async/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Options:
2929
# whether to print an error message even when autofixed
3030
error_on_autofix: bool
3131
no_checkpoint_warning_decorators: Collection[str]
32+
transform_async_generator_decorators: Collection[str]
3233
exception_suppress_context_managers: Collection[str]
3334
startable_in_context_manager: Collection[str]
3435
async200_blocking_calls: dict[str, str]

flake8_async/visitors/visitors.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,19 +401,26 @@ def leave_IfExp_test(self, node: cst.IfExp):
401401
@disabled_by_default
402402
class Visitor900(Flake8AsyncVisitor):
403403
error_codes: Mapping[str, str] = {
404-
"ASYNC900": "Async generator without `@asynccontextmanager` not allowed."
404+
"ASYNC900": "Async generator not allowed, unless transformed by a known decorator"
405405
}
406406

407407
def __init__(self, *args: Any, **kwargs: Any):
408408
super().__init__(*args, **kwargs)
409409
self.unsafe_function: ast.AsyncFunctionDef | None = None
410+
self.transform_decorators = (
411+
"asynccontextmanager", "fixture", *self.options.transform_async_generator_decorators
412+
)
413+
self.error_codes["ASYNC900"] = ( # type: ignore
414+
"Async generator not allowed, unless transformed by a known decorator"
415+
f" (one of: {', '.join(self.transform_decorators)})"
416+
)
410417

411418
def visit_AsyncFunctionDef(
412419
self, node: ast.AsyncFunctionDef | ast.FunctionDef | ast.Lambda
413420
):
414421
self.save_state(node, "unsafe_function")
415422
if isinstance(node, ast.AsyncFunctionDef) and not has_decorator(
416-
node, "asynccontextmanager", "fixture"
423+
node, *self.transform_decorators
417424
):
418425
self.unsafe_function = node
419426
else:

tests/eval_files/async900.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,12 @@ async def cm():
5454
async def another_non_generator():
5555
def foo():
5656
yield
57+
58+
59+
# ARG --transform-async-generator-decorators=this_is_like_a_context_manager
60+
61+
62+
@this_is_like_a_context_manager() # OK because of the config, issue #277
63+
async def some_generator():
64+
while True:
65+
yield

0 commit comments

Comments
 (0)