Skip to content

Commit 4578f06

Browse files
committed
Rename 120->300, add rules notes
1 parent 87f785e commit 4578f06

File tree

5 files changed

+35
-15
lines changed

5 files changed

+35
-15
lines changed

docs/changelog.rst

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

77
24.5.5
88
======
9-
- Add :ref:`ASYNC120 <async120>` create-task-no-reference
9+
- Add :ref:`ASYNC300 <async300>` create-task-no-reference
1010

1111
24.5.4
1212
======

docs/rules.rst

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Rules
66
General rules
77
=============
88

9+
Our ``ASYNC1xx`` rules check for semantic problems ranging from fatal errors (e.g. 101),
10+
to idioms for clearer code (e.g. 116).
11+
912
_`ASYNC100` : cancel-scope-no-checkpoint
1013
A :ref:`timeout_context` does not contain any :ref:`checkpoints <checkpoint>`.
1114
This makes it pointless, as the timeout can only be triggered by a checkpoint.
@@ -70,15 +73,14 @@ _`ASYNC119` : yield-in-cm-in-async-gen
7073
``yield`` in context manager in async generator is unsafe, the cleanup may be delayed until ``await`` is no longer allowed.
7174
We strongly encourage you to read `PEP 533 <https://peps.python.org/pep-0533/>`_ and use `async with aclosing(...) <https://docs.python.org/3/library/contextlib.html#contextlib.aclosing>`_, or better yet avoid async generators entirely (see `ASYNC900`_ ) in favor of context managers which return an iterable :ref:`channel/stream/queue <channel_stream_queue>`.
7275

73-
_`ASYNC120` : create-task-no-reference
74-
Calling :func:`asyncio.create_task` without saving the result. A task that isn't referenced elsewhere may get garbage collected at any time, even before it's done.
75-
Note that this rule won't check whether the variable the result is saved in is susceptible to being garbage-collected itself. See the asyncio documentation for best practices.
76-
You might consider instead using a :ref:`TaskGroup <taskgroup_nursery>` and calling :meth:`asyncio.TaskGroup.create_task` to avoid this problem, and gain the advantages of structured concurrency with e.g. better cancellation semantics.
77-
7876

7977
Blocking sync calls in async functions
8078
======================================
8179

80+
Our 2xx lint rules warn you to use the async equivalent for slow sync calls which
81+
would otherwise block the event loop (and therefore cause performance problems,
82+
or even deadlock).
83+
8284
.. _httpx.Client: https://www.python-httpx.org/api/#client
8385
.. _httpx.AsyncClient: https://www.python-httpx.org/api/#asyncclient
8486
.. _urllib3: https://github.com/urllib3/urllib3
@@ -130,9 +132,27 @@ ASYNC251 : blocking-sleep
130132
Use :func:`trio.sleep`/:func:`anyio.sleep`/:func:`asyncio.sleep`.
131133

132134

135+
Asyncio-specific rules
136+
======================
137+
138+
Asyncio *encourages* structured concurrency, with :obj:`asyncio.TaskGroup`, but does not *require* it.
139+
We therefore provide some additional lint rules for common problems - although we'd also recommend a
140+
gradual migration to AnyIO, which is much less error-prone.
141+
142+
_`ASYNC300` : create-task-no-reference
143+
Calling :func:`asyncio.create_task` without saving the result. A task that isn't referenced elsewhere may get garbage collected at any time, even before it's done.
144+
Note that this rule won't check whether the variable the result is saved in is susceptible to being garbage-collected itself. See the asyncio documentation for best practices.
145+
You might consider instead using a :ref:`TaskGroup <taskgroup_nursery>` and calling :meth:`asyncio.TaskGroup.create_task` to avoid this problem, and gain the advantages of structured concurrency with e.g. better cancellation semantics.
146+
147+
133148
Optional rules disabled by default
134149
==================================
135150

151+
Our 9xx rules check for semantics issues, like 1xx rules, but are disabled by default due
152+
to the higher volume of warnings. We encourage you to enable them - without guaranteed
153+
:ref:`checkpoint`\ s timeouts and cancellation can be arbitrarily delayed, and async
154+
generators are prone to the problems described in :pep:`533`.
155+
136156
_`ASYNC900` : unsafe-async-generator
137157
Async generator without :func:`@asynccontextmanager <contextlib.asynccontextmanager>` not allowed.
138158
You might want to enable this on a codebase since async generators are inherently unsafe and cleanup logic might not be performed.

flake8_async/visitors/visitors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,9 @@ def visit_Yield(self, node: ast.Yield):
342342

343343

344344
@error_class_cst
345-
class Visitor120(Flake8AsyncVisitor_cst):
345+
class Visitor300(Flake8AsyncVisitor_cst):
346346
error_codes: Mapping[str, str] = {
347-
"ASYNC120": "asyncio.create_task() called without saving the result"
347+
"ASYNC300": "asyncio.create_task() called without saving the result"
348348
}
349349

350350
def __init__(self, *args: Any, **kwargs: Any):

tests/eval_files/async120.py renamed to tests/eval_files/async300.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def __iadd__(self, obj: object):
2323

2424
async def foo():
2525
args: Any
26-
asyncio.create_task(*args) # ASYNC120: 4
26+
asyncio.create_task(*args) # ASYNC300: 4
2727

2828
k = asyncio.create_task(*args)
2929

@@ -48,16 +48,16 @@ async def foo():
4848

4949
# more or less esoteric ways of not saving the value
5050

51-
[asyncio.create_task(*args)] # ASYNC120: 5
51+
[asyncio.create_task(*args)] # ASYNC300: 5
5252

53-
(asyncio.create_task(*args) for i in range(10)) # ASYNC120: 5
53+
(asyncio.create_task(*args) for i in range(10)) # ASYNC300: 5
5454

55-
args = 1 if asyncio.create_task(*args) else 2 # ASYNC120: 16
55+
args = 1 if asyncio.create_task(*args) else 2 # ASYNC300: 16
5656

57-
args = (i for i in range(10) if asyncio.create_task(*args)) # ASYNC120: 36
57+
args = (i for i in range(10) if asyncio.create_task(*args)) # ASYNC300: 36
5858

5959
# not supported, it can't be used as a context manager
60-
with asyncio.create_task(*args) as k: # type: ignore[attr-defined] # ASYNC120: 9
60+
with asyncio.create_task(*args) as k: # type: ignore[attr-defined] # ASYNC300: 9
6161
...
6262

6363
# import aliasing is not supported (this would raise ASYNC106 bad-async-library-import)

tests/test_flake8_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ def _parse_eval_file(
476476
"ASYNC116",
477477
"ASYNC117",
478478
"ASYNC118",
479-
"ASYNC120",
479+
"ASYNC300",
480480
"ASYNC912",
481481
}
482482

0 commit comments

Comments
 (0)