flake8-async 24.3.4__tar.gz → 24.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flake8-async-24.3.4 → flake8-async-24.3.6}/CHANGELOG.md +7 -1
- {flake8-async-24.3.4 → flake8-async-24.3.6}/PKG-INFO +10 -4
- {flake8-async-24.3.4 → flake8-async-24.3.6}/README.md +2 -2
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/__init__.py +1 -1
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor100.py +3 -1
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor102.py +29 -5
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async.egg-info/PKG-INFO +10 -4
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async.egg-info/SOURCES.txt +2 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/autofix_files/async100.py +6 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/autofix_files/noqa.py +19 -9
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async100.py +6 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async102.py +13 -0
- flake8-async-24.3.6/tests/eval_files/async102_aclose.py +22 -0
- flake8-async-24.3.6/tests/eval_files/async102_aclose_args.py +24 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/noqa.py +17 -7
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_messages_documented.py +3 -1
- {flake8-async-24.3.4 → flake8-async-24.3.6}/CONTRIBUTING.md +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/LICENSE +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/MANIFEST.in +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/__main__.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/base.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/runner.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/__init__.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/flake8asyncvisitor.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/helpers.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor101.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor103_104.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor105.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor111.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor118.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor2xx.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor91x.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitor_utility.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async/visitors/visitors.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async.egg-info/dependency_links.txt +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async.egg-info/entry_points.txt +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async.egg-info/not-zip-safe +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async.egg-info/requires.txt +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/flake8_async.egg-info/top_level.txt +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/pyproject.toml +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/setup.cfg +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/setup.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/__init__.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/autofix_files/async100_simple_autofix.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/autofix_files/async910.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/autofix_files/async911.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/autofix_files/async91x_autofix.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/autofix_files/noqa_testing.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/conftest.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/anyio_trio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async100_asyncio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async100_noautofix.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async100_simple_autofix.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async101.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async102_anyio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async102_asyncio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async102_trio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async103.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async103_all_imported.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async103_both_imported.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async103_no_104.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async103_trio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async104.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async104_anyio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async104_trio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async105.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async105_anyio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async106.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async109.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async110.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async111.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async112.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async113.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async113_anyio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async113_trio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async114.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async115.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async116.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async118.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async200.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async210.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async211.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async212.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async22x.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async22x_asyncio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async232.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async232_asyncio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async23x.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async23x_asyncio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async240.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async250.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async250_multi_library.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async251.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async251_multi_library.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async900.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async910.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async911.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async91x_autofix.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/async91x_noautofix.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/no_library.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/noqa_no_autofix.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/noqa_testing.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/eval_files/trio_anyio.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_all_visitors_imported.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_changelog_and_version.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_config_and_args.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_decorator.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_exception_on_invalid_code.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_flake8_async.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/test_formatting.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tests/trio_options.py +0 -0
- {flake8-async-24.3.4 → flake8-async-24.3.6}/tox.ini +0 -0
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
*[CalVer, YY.month.patch](https://calver.org/)*
|
|
3
3
|
|
|
4
|
+
## 24.3.6
|
|
5
|
+
- ASYNC100 no longer triggers if a context manager contains a `yield`.
|
|
6
|
+
|
|
7
|
+
## 24.3.5
|
|
8
|
+
- ASYNC102 (no await inside finally or critical except) no longer raises warnings for calls to `aclose()` on objects in trio/anyio code. See https://github.com/python-trio/flake8-async/issues/156
|
|
9
|
+
|
|
4
10
|
## 24.3.4
|
|
5
|
-
- ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()
|
|
11
|
+
- ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()`.
|
|
6
12
|
|
|
7
13
|
## 24.3.3
|
|
8
14
|
- Add ASYNC251: `time.sleep()` in async method.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version: 24.3.
|
|
3
|
+
Version: 24.3.6
|
|
4
4
|
Summary: A highly opinionated flake8 plugin for Trio-related problems.
|
|
5
5
|
Home-page: https://github.com/python-trio/flake8-async
|
|
6
6
|
Author: Zac Hatfield-Dodds, John Litborn, and Contributors
|
|
@@ -50,7 +50,7 @@ pip install flake8-async
|
|
|
50
50
|
## List of warnings
|
|
51
51
|
- **ASYNC100**: A `with [trio/anyio].fail_after(...):` or `with [trio/anyio].move_on_after(...):`
|
|
52
52
|
context does not contain any `await` statements. This makes it pointless, as
|
|
53
|
-
the timeout can only be triggered by a checkpoint.
|
|
53
|
+
the timeout can only be triggered by a checkpoint. This check also allows `yield` statements, since checkpoints can happen in the caller we yield to.
|
|
54
54
|
- **ASYNC101**: `yield` inside a trio/anyio nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling.
|
|
55
55
|
- **ASYNC102**: It's unsafe to await inside `finally:` or `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError` unless you use a shielded cancel scope with a timeout. This is currently not able to detect asyncio shields.
|
|
56
56
|
- **ASYNC103**: `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError`, or a bare `except:` with a code path that doesn't re-raise. If you don't want to re-raise `BaseException`, add a separate handler for `trio.Cancelled`/`anyio.get_cancelled_exc_class()`/`asyncio.exceptions.CancelledError` before.
|
|
@@ -109,7 +109,7 @@ adding the following to your `.pre-commit-config.yaml`:
|
|
|
109
109
|
minimum_pre_commit_version: '2.9.0'
|
|
110
110
|
repos:
|
|
111
111
|
- repo: https://github.com/python-trio/flake8-async
|
|
112
|
-
rev: 24.3.
|
|
112
|
+
rev: 24.3.6
|
|
113
113
|
hooks:
|
|
114
114
|
- id: flake8-async
|
|
115
115
|
# args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
|
|
@@ -221,8 +221,14 @@ async def my_function():
|
|
|
221
221
|
# Changelog
|
|
222
222
|
*[CalVer, YY.month.patch](https://calver.org/)*
|
|
223
223
|
|
|
224
|
+
## 24.3.6
|
|
225
|
+
- ASYNC100 no longer triggers if a context manager contains a `yield`.
|
|
226
|
+
|
|
227
|
+
## 24.3.5
|
|
228
|
+
- ASYNC102 (no await inside finally or critical except) no longer raises warnings for calls to `aclose()` on objects in trio/anyio code. See https://github.com/python-trio/flake8-async/issues/156
|
|
229
|
+
|
|
224
230
|
## 24.3.4
|
|
225
|
-
- ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()
|
|
231
|
+
- ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()`.
|
|
226
232
|
|
|
227
233
|
## 24.3.3
|
|
228
234
|
- Add ASYNC251: `time.sleep()` in async method.
|
|
@@ -25,7 +25,7 @@ pip install flake8-async
|
|
|
25
25
|
## List of warnings
|
|
26
26
|
- **ASYNC100**: A `with [trio/anyio].fail_after(...):` or `with [trio/anyio].move_on_after(...):`
|
|
27
27
|
context does not contain any `await` statements. This makes it pointless, as
|
|
28
|
-
the timeout can only be triggered by a checkpoint.
|
|
28
|
+
the timeout can only be triggered by a checkpoint. This check also allows `yield` statements, since checkpoints can happen in the caller we yield to.
|
|
29
29
|
- **ASYNC101**: `yield` inside a trio/anyio nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling.
|
|
30
30
|
- **ASYNC102**: It's unsafe to await inside `finally:` or `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError` unless you use a shielded cancel scope with a timeout. This is currently not able to detect asyncio shields.
|
|
31
31
|
- **ASYNC103**: `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError`, or a bare `except:` with a code path that doesn't re-raise. If you don't want to re-raise `BaseException`, add a separate handler for `trio.Cancelled`/`anyio.get_cancelled_exc_class()`/`asyncio.exceptions.CancelledError` before.
|
|
@@ -84,7 +84,7 @@ adding the following to your `.pre-commit-config.yaml`:
|
|
|
84
84
|
minimum_pre_commit_version: '2.9.0'
|
|
85
85
|
repos:
|
|
86
86
|
- repo: https://github.com/python-trio/flake8-async
|
|
87
|
-
rev: 24.3.
|
|
87
|
+
rev: 24.3.6
|
|
88
88
|
hooks:
|
|
89
89
|
- id: flake8-async
|
|
90
90
|
# args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
|
|
@@ -74,9 +74,11 @@ class Visitor100_libcst(Flake8AsyncVisitor_cst):
|
|
|
74
74
|
if node.asynchronous is not None:
|
|
75
75
|
self.checkpoint()
|
|
76
76
|
|
|
77
|
-
def visit_Await(self, node: cst.Await | cst.
|
|
77
|
+
def visit_Await(self, node: cst.Await | cst.Yield):
|
|
78
78
|
self.checkpoint()
|
|
79
79
|
|
|
80
|
+
visit_Yield = visit_Await
|
|
81
|
+
|
|
80
82
|
def visit_FunctionDef(self, node: cst.FunctionDef):
|
|
81
83
|
self.save_state(node, "has_checkpoint_stack", copy=True)
|
|
82
84
|
self.has_checkpoint_stack = []
|
|
@@ -52,15 +52,35 @@ class Visitor102(Flake8AsyncVisitor):
|
|
|
52
52
|
self._trio_context_managers: list[Visitor102.TrioScope] = []
|
|
53
53
|
self.cancelled_caught = False
|
|
54
54
|
|
|
55
|
-
# if we're inside a finally, and we're not inside a scope that
|
|
56
|
-
# both a timeout and shield
|
|
57
|
-
def
|
|
55
|
+
# if we're inside a finally or critical except, and we're not inside a scope that
|
|
56
|
+
# doesn't have both a timeout and shield
|
|
57
|
+
def async_call_checker(
|
|
58
|
+
self, node: ast.Await | ast.AsyncFor | ast.AsyncWith
|
|
59
|
+
) -> None:
|
|
58
60
|
if self._critical_scope is not None and not any(
|
|
59
61
|
cm.has_timeout and cm.shielded for cm in self._trio_context_managers
|
|
60
62
|
):
|
|
61
63
|
self.error(node, self._critical_scope)
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
def is_safe_aclose_call(self, node: ast.Await) -> bool:
|
|
66
|
+
return (
|
|
67
|
+
# don't mark calls safe in asyncio-only files
|
|
68
|
+
# a more defensive option would be `asyncio not in self.library`
|
|
69
|
+
self.library != ("asyncio",)
|
|
70
|
+
and isinstance(node.value, ast.Call)
|
|
71
|
+
# only known safe if no arguments
|
|
72
|
+
and not node.value.args
|
|
73
|
+
and not node.value.keywords
|
|
74
|
+
and isinstance(node.value.func, ast.Attribute)
|
|
75
|
+
and node.value.func.attr == "aclose"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def visit_Await(self, node: ast.Await):
|
|
79
|
+
# allow calls to `.aclose()`
|
|
80
|
+
if not (self.is_safe_aclose_call(node)):
|
|
81
|
+
self.async_call_checker(node)
|
|
82
|
+
|
|
83
|
+
visit_AsyncFor = async_call_checker
|
|
64
84
|
|
|
65
85
|
def visit_With(self, node: ast.With | ast.AsyncWith):
|
|
66
86
|
self.save_state(node, "_trio_context_managers", copy=True)
|
|
@@ -82,7 +102,7 @@ class Visitor102(Flake8AsyncVisitor):
|
|
|
82
102
|
break
|
|
83
103
|
|
|
84
104
|
def visit_AsyncWith(self, node: ast.AsyncWith):
|
|
85
|
-
self.
|
|
105
|
+
self.async_call_checker(node)
|
|
86
106
|
self.visit_With(node)
|
|
87
107
|
|
|
88
108
|
def visit_Try(self, node: ast.Try):
|
|
@@ -94,6 +114,10 @@ class Visitor102(Flake8AsyncVisitor):
|
|
|
94
114
|
self.visit_nodes(node.body, node.handlers, node.orelse)
|
|
95
115
|
|
|
96
116
|
self._trio_context_managers = []
|
|
117
|
+
# node.finalbody does not have a lineno, so we give the position of the try
|
|
118
|
+
# it'd be possible to estimate the lineno given the last except and the first
|
|
119
|
+
# statement in the finally, but it would be very hard to get it perfect with
|
|
120
|
+
# comments and empty lines and stuff.
|
|
97
121
|
self._critical_scope = Statement("try/finally", node.lineno, node.col_offset)
|
|
98
122
|
self.visit_nodes(node.finalbody)
|
|
99
123
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version: 24.3.
|
|
3
|
+
Version: 24.3.6
|
|
4
4
|
Summary: A highly opinionated flake8 plugin for Trio-related problems.
|
|
5
5
|
Home-page: https://github.com/python-trio/flake8-async
|
|
6
6
|
Author: Zac Hatfield-Dodds, John Litborn, and Contributors
|
|
@@ -50,7 +50,7 @@ pip install flake8-async
|
|
|
50
50
|
## List of warnings
|
|
51
51
|
- **ASYNC100**: A `with [trio/anyio].fail_after(...):` or `with [trio/anyio].move_on_after(...):`
|
|
52
52
|
context does not contain any `await` statements. This makes it pointless, as
|
|
53
|
-
the timeout can only be triggered by a checkpoint.
|
|
53
|
+
the timeout can only be triggered by a checkpoint. This check also allows `yield` statements, since checkpoints can happen in the caller we yield to.
|
|
54
54
|
- **ASYNC101**: `yield` inside a trio/anyio nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling.
|
|
55
55
|
- **ASYNC102**: It's unsafe to await inside `finally:` or `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError` unless you use a shielded cancel scope with a timeout. This is currently not able to detect asyncio shields.
|
|
56
56
|
- **ASYNC103**: `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError`, or a bare `except:` with a code path that doesn't re-raise. If you don't want to re-raise `BaseException`, add a separate handler for `trio.Cancelled`/`anyio.get_cancelled_exc_class()`/`asyncio.exceptions.CancelledError` before.
|
|
@@ -109,7 +109,7 @@ adding the following to your `.pre-commit-config.yaml`:
|
|
|
109
109
|
minimum_pre_commit_version: '2.9.0'
|
|
110
110
|
repos:
|
|
111
111
|
- repo: https://github.com/python-trio/flake8-async
|
|
112
|
-
rev: 24.3.
|
|
112
|
+
rev: 24.3.6
|
|
113
113
|
hooks:
|
|
114
114
|
- id: flake8-async
|
|
115
115
|
# args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
|
|
@@ -221,8 +221,14 @@ async def my_function():
|
|
|
221
221
|
# Changelog
|
|
222
222
|
*[CalVer, YY.month.patch](https://calver.org/)*
|
|
223
223
|
|
|
224
|
+
## 24.3.6
|
|
225
|
+
- ASYNC100 no longer triggers if a context manager contains a `yield`.
|
|
226
|
+
|
|
227
|
+
## 24.3.5
|
|
228
|
+
- ASYNC102 (no await inside finally or critical except) no longer raises warnings for calls to `aclose()` on objects in trio/anyio code. See https://github.com/python-trio/flake8-async/issues/156
|
|
229
|
+
|
|
224
230
|
## 24.3.4
|
|
225
|
-
- ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()
|
|
231
|
+
- ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()`.
|
|
226
232
|
|
|
227
233
|
## 24.3.3
|
|
228
234
|
- Add ASYNC251: `time.sleep()` in async method.
|
|
@@ -56,6 +56,8 @@ tests/eval_files/async100_noautofix.py
|
|
|
56
56
|
tests/eval_files/async100_simple_autofix.py
|
|
57
57
|
tests/eval_files/async101.py
|
|
58
58
|
tests/eval_files/async102.py
|
|
59
|
+
tests/eval_files/async102_aclose.py
|
|
60
|
+
tests/eval_files/async102_aclose_args.py
|
|
59
61
|
tests/eval_files/async102_anyio.py
|
|
60
62
|
tests/eval_files/async102_asyncio.py
|
|
61
63
|
tests/eval_files/async102_trio.py
|
|
@@ -1,47 +1,57 @@
|
|
|
1
1
|
# AUTOFIX
|
|
2
2
|
# NOASYNCIO - does not trigger on ASYNC100
|
|
3
|
-
# ARG --enable=ASYNC100,ASYNC911
|
|
3
|
+
# ARG --enable=ASYNC100,ASYNC911,ASYNC910
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
import trio
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
def foo() -> bool:
|
|
10
|
+
return False
|
|
11
|
+
|
|
12
|
+
|
|
9
13
|
# fmt: off
|
|
10
14
|
async def foo_no_noqa():
|
|
11
|
-
await trio.lowlevel.checkpoint()
|
|
12
15
|
# ASYNC100: 9, 'trio', 'fail_after'
|
|
13
|
-
|
|
16
|
+
if foo():
|
|
17
|
+
await trio.lowlevel.checkpoint()
|
|
18
|
+
return # ASYNC910: 12, "return", Statement("function definition", lineno-3)
|
|
14
19
|
await trio.lowlevel.checkpoint()
|
|
15
20
|
|
|
16
21
|
|
|
17
22
|
async def foo_noqa_bare():
|
|
18
23
|
with trio.fail_after(5): # noqa
|
|
19
|
-
|
|
24
|
+
if foo():
|
|
25
|
+
return # noqa
|
|
20
26
|
await trio.lowlevel.checkpoint()
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
async def foo_noqa_100():
|
|
24
30
|
with trio.fail_after(5): # noqa: ASYNC100
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
if foo():
|
|
32
|
+
await trio.lowlevel.checkpoint()
|
|
33
|
+
return # ASYNC910: 12, "return", Statement("function definition", lineno-3)
|
|
27
34
|
await trio.lowlevel.checkpoint()
|
|
28
35
|
|
|
29
36
|
|
|
30
37
|
async def foo_noqa_911():
|
|
31
38
|
# ASYNC100: 9, 'trio', 'fail_after'
|
|
32
|
-
|
|
39
|
+
if foo():
|
|
40
|
+
return # noqa: ASYNC910
|
|
33
41
|
await trio.lowlevel.checkpoint()
|
|
34
42
|
|
|
35
43
|
|
|
36
44
|
async def foo_noqa_100_911():
|
|
37
45
|
with trio.fail_after(5): # noqa: ASYNC100, ASYNC911
|
|
38
|
-
|
|
46
|
+
if foo():
|
|
47
|
+
return # noqa: ASYNC910
|
|
39
48
|
await trio.lowlevel.checkpoint()
|
|
40
49
|
|
|
41
50
|
|
|
42
51
|
async def foo_noqa_100_911_500():
|
|
43
52
|
with trio.fail_after(5): # noqa: ASYNC100, ASYNC911 , ASYNC500,,,
|
|
44
|
-
|
|
53
|
+
if foo():
|
|
54
|
+
return # noqa: ASYNC100, ASYNC910 , ASYNC500,,,
|
|
45
55
|
await trio.lowlevel.checkpoint()
|
|
46
56
|
# fmt: on
|
|
47
57
|
|
|
@@ -252,3 +252,16 @@ async def foo_nested_excepts():
|
|
|
252
252
|
await foo()
|
|
253
253
|
await foo()
|
|
254
254
|
await foo()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# double check that this works nested inside an async for
|
|
258
|
+
async def foo_nested_async_for():
|
|
259
|
+
|
|
260
|
+
async for i in trio.bypasslinters:
|
|
261
|
+
try:
|
|
262
|
+
...
|
|
263
|
+
except BaseException:
|
|
264
|
+
async for ( # error: 12, Statement("BaseException", lineno-1)
|
|
265
|
+
j
|
|
266
|
+
) in trio.bypasslinters:
|
|
267
|
+
...
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
|
|
3
|
+
# exclude finally: await x.aclose() from async102, with trio/anyio
|
|
4
|
+
# ANYIO_NO_ERROR
|
|
5
|
+
# TRIO_NO_ERROR
|
|
6
|
+
# See also async102_aclose_args.py - which makes sure trio/anyio raises errors if there
|
|
7
|
+
# are arguments to aclose().
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def foo():
|
|
11
|
+
# no type tracking in this check, we allow any call that looks like
|
|
12
|
+
# `await [...].aclose()`
|
|
13
|
+
x = None
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
...
|
|
17
|
+
except BaseException:
|
|
18
|
+
await x.aclose() # ASYNC102: 8, Statement("BaseException", lineno-1)
|
|
19
|
+
await x.y.aclose() # ASYNC102: 8, Statement("BaseException", lineno-2)
|
|
20
|
+
finally:
|
|
21
|
+
await x.aclose() # ASYNC102: 8, Statement("try/finally", lineno-6)
|
|
22
|
+
await x.y.aclose() # ASYNC102: 8, Statement("try/finally", lineno-7)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
|
|
3
|
+
# trio/anyio should still raise errors if there's args
|
|
4
|
+
# asyncio will always raise errors
|
|
5
|
+
|
|
6
|
+
# See also async102_aclose.py, which checks that trio/anyio marks arg-less aclose() as safe
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def foo():
|
|
10
|
+
# no type tracking in this check
|
|
11
|
+
x = None
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
...
|
|
15
|
+
except BaseException:
|
|
16
|
+
await x.aclose(foo) # ASYNC102: 8, Statement("BaseException", lineno-1)
|
|
17
|
+
await x.aclose(bar=foo) # ASYNC102: 8, Statement("BaseException", lineno-2)
|
|
18
|
+
await x.aclose(*foo) # ASYNC102: 8, Statement("BaseException", lineno-3)
|
|
19
|
+
await x.aclose(None) # ASYNC102: 8, Statement("BaseException", lineno-4)
|
|
20
|
+
finally:
|
|
21
|
+
await x.aclose(foo) # ASYNC102: 8, Statement("try/finally", lineno-8)
|
|
22
|
+
await x.aclose(bar=foo) # ASYNC102: 8, Statement("try/finally", lineno-9)
|
|
23
|
+
await x.aclose(*foo) # ASYNC102: 8, Statement("try/finally", lineno-10)
|
|
24
|
+
await x.aclose(None) # ASYNC102: 8, Statement("try/finally", lineno-11)
|
|
@@ -1,45 +1,55 @@
|
|
|
1
1
|
# AUTOFIX
|
|
2
2
|
# NOASYNCIO - does not trigger on ASYNC100
|
|
3
|
-
# ARG --enable=ASYNC100,ASYNC911
|
|
3
|
+
# ARG --enable=ASYNC100,ASYNC911,ASYNC910
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
import trio
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
def foo() -> bool:
|
|
10
|
+
return False
|
|
11
|
+
|
|
12
|
+
|
|
9
13
|
# fmt: off
|
|
10
14
|
async def foo_no_noqa():
|
|
11
15
|
with trio.fail_after(5): # ASYNC100: 9, 'trio', 'fail_after'
|
|
12
|
-
|
|
16
|
+
if foo():
|
|
17
|
+
return # ASYNC910: 12, "return", Statement("function definition", lineno-3)
|
|
13
18
|
await trio.lowlevel.checkpoint()
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
async def foo_noqa_bare():
|
|
17
22
|
with trio.fail_after(5): # noqa
|
|
18
|
-
|
|
23
|
+
if foo():
|
|
24
|
+
return # noqa
|
|
19
25
|
await trio.lowlevel.checkpoint()
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
async def foo_noqa_100():
|
|
23
29
|
with trio.fail_after(5): # noqa: ASYNC100
|
|
24
|
-
|
|
30
|
+
if foo():
|
|
31
|
+
return # ASYNC910: 12, "return", Statement("function definition", lineno-3)
|
|
25
32
|
await trio.lowlevel.checkpoint()
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
async def foo_noqa_911():
|
|
29
36
|
with trio.fail_after(5): # ASYNC100: 9, 'trio', 'fail_after'
|
|
30
|
-
|
|
37
|
+
if foo():
|
|
38
|
+
return # noqa: ASYNC910
|
|
31
39
|
await trio.lowlevel.checkpoint()
|
|
32
40
|
|
|
33
41
|
|
|
34
42
|
async def foo_noqa_100_911():
|
|
35
43
|
with trio.fail_after(5): # noqa: ASYNC100, ASYNC911
|
|
36
|
-
|
|
44
|
+
if foo():
|
|
45
|
+
return # noqa: ASYNC910
|
|
37
46
|
await trio.lowlevel.checkpoint()
|
|
38
47
|
|
|
39
48
|
|
|
40
49
|
async def foo_noqa_100_911_500():
|
|
41
50
|
with trio.fail_after(5): # noqa: ASYNC100, ASYNC911 , ASYNC500,,,
|
|
42
|
-
|
|
51
|
+
if foo():
|
|
52
|
+
return # noqa: ASYNC100, ASYNC910 , ASYNC500,,,
|
|
43
53
|
await trio.lowlevel.checkpoint()
|
|
44
54
|
# fmt: on
|
|
45
55
|
|
|
@@ -44,7 +44,9 @@ def test_messages_documented():
|
|
|
44
44
|
if not file_path.is_file():
|
|
45
45
|
continue
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
# only look in the stem (final part of the path), so as not to get tripped
|
|
48
|
+
# up by [git worktree] directories with an exception code in the name
|
|
49
|
+
if m := re.search(r"^async\d\d\d", str(file_path.stem)):
|
|
48
50
|
documented_errors["eval_files"].add(m.group().upper())
|
|
49
51
|
|
|
50
52
|
with open(file_path) as file:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|