flake8-async 24.11.3__tar.gz → 25.2.2__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.11.3 → flake8_async-25.2.2}/PKG-INFO +12 -3
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/__init__.py +1 -1
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor103_104.py +2 -2
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor2xx.py +4 -2
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor91x.py +208 -31
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitors.py +33 -16
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async.egg-info/PKG-INFO +12 -3
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async.egg-info/SOURCES.txt +6 -8
- {flake8_async-24.11.3 → flake8_async-25.2.2}/pyproject.toml +3 -2
- {flake8_async-24.11.3 → flake8_async-25.2.2}/setup.py +1 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async100.py +74 -6
- flake8_async-25.2.2/tests/autofix_files/async124.py +159 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async910.py +21 -2
- flake8_async-25.2.2/tests/autofix_files/async913_trio_anyio.py +12 -0
- flake8_async-24.11.3/tests/test_changelog_and_version.py → flake8_async-25.2.2/tests/check_changelog_and_version.py +26 -10
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async100.py +74 -6
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async101.py +11 -4
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async111.py +0 -2
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async112.py +0 -1
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async112_asyncio.py +1 -1
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async113.py +32 -15
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async113_trio.py +5 -9
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async121.py +0 -1
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async121_anyio.py +0 -1
- flake8_async-25.2.2/tests/eval_files/async124.py +145 -0
- flake8_async-25.2.2/tests/eval_files/async124_no_autofix.py +9 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async251.py +0 -2
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async910.py +20 -2
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async912.py +8 -1
- flake8_async-25.2.2/tests/eval_files/async913_trio_anyio.py +11 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/test_flake8_async.py +25 -2
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/trio_options.py +8 -1
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tox.ini +1 -1
- flake8_async-24.11.3/tests/autofix_files/async100_trio.py +0 -10
- flake8_async-24.11.3/tests/eval_files/async100_trio.py +0 -10
- flake8_async-24.11.3/tests/eval_files/async101_anyio.py +0 -10
- flake8_async-24.11.3/tests/eval_files/async101_trio.py +0 -17
- flake8_async-24.11.3/tests/eval_files/async111_anyio.py +0 -21
- flake8_async-24.11.3/tests/eval_files/async112_anyio.py +0 -28
- flake8_async-24.11.3/tests/eval_files/async113_anyio.py +0 -22
- {flake8_async-24.11.3 → flake8_async-25.2.2}/CHANGELOG.md +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/CONTRIBUTING.md +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/LICENSE +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/MANIFEST.in +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/README.md +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/__main__.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/base.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/runner.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/__init__.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/flake8asyncvisitor.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/helpers.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor101.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor102_120.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor105.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor111.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor118.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor123.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async/visitors/visitor_utility.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async.egg-info/dependency_links.txt +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async.egg-info/entry_points.txt +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async.egg-info/not-zip-safe +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async.egg-info/requires.txt +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/flake8_async.egg-info/top_level.txt +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/setup.cfg +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/__init__.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async100_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async100_simple_autofix.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async910_insert_library.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async911.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async911_insert_library.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async913.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/async91x_autofix.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/exception_suppress_context_manager.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/exception_suppress_context_manager_import_star.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/noqa.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/autofix_files/noqa_testing.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/conftest.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/anyio_trio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async100_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async100_noautofix.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async100_simple_autofix.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async101_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async102.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async102_aclose.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async102_aclose_args.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async102_anyio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async102_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async102_trio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async103.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async103_all_imported.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async103_both_imported.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async103_no_104.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async103_trio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async104.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async104_anyio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async104_trio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async105.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async105_anyio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async106.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async109.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async110.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async111_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async113_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async114.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async115.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async116.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async118.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async119.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async120.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async121_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async122.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async123.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async123_py311.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async200.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async210.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async211.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async212.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async22x.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async22x_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async232.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async232_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async23x.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async23x_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async240.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async250.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async250_multi_library.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async251_multi_library.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async300.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async900.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async910_insert_library.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async911.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async911_insert_library.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async912_asyncio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async913.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async91x_autofix.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/async91x_noautofix.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/exception_suppress_context_manager.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/exception_suppress_context_manager_import_star.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/no_library.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/noqa.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/noqa_no_autofix.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/noqa_testing.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/eval_files/trio_anyio.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/test_all_visitors_imported.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/test_config_and_args.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/test_decorator.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/test_exception_on_invalid_code.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/test_formatting.py +0 -0
- {flake8_async-24.11.3 → flake8_async-25.2.2}/tests/test_messages_documented.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version:
|
|
3
|
+
Version: 25.2.2
|
|
4
4
|
Summary: A highly opinionated flake8 plugin for Trio-related problems.
|
|
5
5
|
Author: Zac Hatfield-Dodds, John Litborn, and Contributors
|
|
6
6
|
Author-email: zac@zhd.dev
|
|
@@ -21,9 +21,18 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Requires-Python: >=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
|
-
License-File: LICENSE
|
|
25
24
|
Requires-Dist: flake8>=6
|
|
26
25
|
Requires-Dist: libcst>=1.0.1
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: license
|
|
32
|
+
Dynamic: project-url
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
27
36
|
|
|
28
37
|
[](https://flake8-async.readthedocs.io)
|
|
29
38
|
[](https://pypi.org/project/flake8-async)
|
|
@@ -51,9 +51,9 @@ _error_codes = {
|
|
|
51
51
|
"ASYNC103": _async103_common_msg,
|
|
52
52
|
"ASYNC104": "Cancelled (and therefore BaseException) must be re-raised.",
|
|
53
53
|
}
|
|
54
|
-
for poss_library in _suggestion_dict:
|
|
54
|
+
for poss_library, _lib_suggest in _suggestion_dict.items():
|
|
55
55
|
_error_codes[f"ASYNC103_{'_'.join(poss_library)}"] = (
|
|
56
|
-
_async103_common_msg + _suggestion.format(
|
|
56
|
+
_async103_common_msg + _suggestion.format(_lib_suggest)
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
|
|
@@ -186,7 +186,9 @@ class Visitor22X(Visitor200):
|
|
|
186
186
|
"Sync call {} in async function, use "
|
|
187
187
|
"`asyncio.create_subprocess_[exec/shell]."
|
|
188
188
|
),
|
|
189
|
-
"ASYNC222":
|
|
189
|
+
"ASYNC222": (
|
|
190
|
+
"Sync call {} in async function, wrap in `{}.to_thread.run_sync()`."
|
|
191
|
+
),
|
|
190
192
|
"ASYNC222_asyncio": (
|
|
191
193
|
"Sync call {} in async function, use `asyncio.loop.run_in_executor`."
|
|
192
194
|
),
|
|
@@ -397,7 +399,7 @@ wrappers: Mapping[str, str] = {
|
|
|
397
399
|
@error_class
|
|
398
400
|
class Visitor25X(Visitor200):
|
|
399
401
|
error_codes: Mapping[str, str] = {
|
|
400
|
-
"ASYNC250":
|
|
402
|
+
"ASYNC250": "Blocking sync call `input()` in async function. Wrap in `{}`.",
|
|
401
403
|
"ASYNC251": (
|
|
402
404
|
"Blocking sync call `time.sleep(...)` in async function."
|
|
403
405
|
" Use `await {}.sleep(...)`."
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
"""Contains Visitor91X.
|
|
1
|
+
"""Contains Visitor91X and Visitor124.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Visitor91X contains checks for
|
|
4
|
+
* ASYNC100 cancel-scope-no-checkpoint
|
|
5
|
+
* ASYNC910 async-function-no-checkpoint
|
|
6
|
+
* ASYNC911 async-generator-no-checkpoint
|
|
7
|
+
* ASYNC912 cancel-scope-no-guaranteed-checkpoint
|
|
8
|
+
* ASYNC913 indefinite-loop-no-guaranteed-checkpoint
|
|
9
|
+
|
|
10
|
+
Visitor124 contains
|
|
11
|
+
* ASYNC124 async-function-could-be-sync
|
|
6
12
|
"""
|
|
7
13
|
|
|
8
14
|
from __future__ import annotations
|
|
@@ -25,6 +31,7 @@ from .helpers import (
|
|
|
25
31
|
flatten_preserving_comments,
|
|
26
32
|
fnmatch_qualified_name_cst,
|
|
27
33
|
func_has_decorator,
|
|
34
|
+
identifier_to_string,
|
|
28
35
|
iter_guaranteed_once_cst,
|
|
29
36
|
with_has_call,
|
|
30
37
|
)
|
|
@@ -63,6 +70,92 @@ def func_empty_body(node: cst.FunctionDef) -> bool:
|
|
|
63
70
|
)
|
|
64
71
|
|
|
65
72
|
|
|
73
|
+
# this could've been implemented as part of visitor91x, but /shrug
|
|
74
|
+
@error_class_cst
|
|
75
|
+
class Visitor124(Flake8AsyncVisitor_cst):
|
|
76
|
+
error_codes: Mapping[str, str] = {
|
|
77
|
+
"ASYNC124": (
|
|
78
|
+
"Async function with no `await` could be sync."
|
|
79
|
+
" Async functions are more expensive to call."
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
|
84
|
+
super().__init__(*args, **kwargs)
|
|
85
|
+
self.has_await = False
|
|
86
|
+
self.in_class = False
|
|
87
|
+
|
|
88
|
+
def visit_ClassDef(self, node: cst.ClassDef):
|
|
89
|
+
self.save_state(node, "in_class", copy=False)
|
|
90
|
+
self.in_class = True
|
|
91
|
+
|
|
92
|
+
def leave_ClassDef(
|
|
93
|
+
self, original_node: cst.ClassDef, updated_node: cst.ClassDef
|
|
94
|
+
) -> cst.ClassDef:
|
|
95
|
+
self.restore_state(original_node)
|
|
96
|
+
return updated_node
|
|
97
|
+
|
|
98
|
+
# await in sync defs are not valid, but handling this will make ASYNC124
|
|
99
|
+
# correctly pop up in parent func as if the child function was async
|
|
100
|
+
def visit_FunctionDef(self, node: cst.FunctionDef):
|
|
101
|
+
# default values are evaluated in parent scope
|
|
102
|
+
# this visitor has no autofixes, so we can throw away return value
|
|
103
|
+
_ = node.params.visit(self)
|
|
104
|
+
|
|
105
|
+
self.save_state(node, "has_await", "in_class", copy=False)
|
|
106
|
+
|
|
107
|
+
# ignore class methods
|
|
108
|
+
self.has_await = self.in_class
|
|
109
|
+
|
|
110
|
+
# but not nested functions
|
|
111
|
+
self.in_class = False
|
|
112
|
+
|
|
113
|
+
_ = node.body.visit(self)
|
|
114
|
+
|
|
115
|
+
# we've manually visited subnodes (that we care about).
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
def leave_FunctionDef(
|
|
119
|
+
self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef
|
|
120
|
+
) -> cst.FunctionDef:
|
|
121
|
+
if (
|
|
122
|
+
original_node.asynchronous is not None
|
|
123
|
+
and not self.has_await
|
|
124
|
+
and not func_empty_body(original_node)
|
|
125
|
+
and not func_has_decorator(original_node, "overload")
|
|
126
|
+
# skip functions with @fixture and params since they may be relying
|
|
127
|
+
# on async fixtures.
|
|
128
|
+
and not (
|
|
129
|
+
original_node.params.params
|
|
130
|
+
and func_has_decorator(original_node, "fixture")
|
|
131
|
+
)
|
|
132
|
+
# ignore functions with no_checkpoint_warning_decorators
|
|
133
|
+
and not fnmatch_qualified_name_cst(
|
|
134
|
+
original_node.decorators, *self.options.no_checkpoint_warning_decorators
|
|
135
|
+
)
|
|
136
|
+
):
|
|
137
|
+
self.error(original_node)
|
|
138
|
+
self.restore_state(original_node)
|
|
139
|
+
return updated_node
|
|
140
|
+
|
|
141
|
+
def visit_Await(self, node: cst.Await):
|
|
142
|
+
self.has_await = True
|
|
143
|
+
|
|
144
|
+
def visit_With(self, node: cst.With | cst.For | cst.CompFor):
|
|
145
|
+
if node.asynchronous is not None:
|
|
146
|
+
self.has_await = True
|
|
147
|
+
|
|
148
|
+
visit_For = visit_With
|
|
149
|
+
visit_CompFor = visit_With
|
|
150
|
+
|
|
151
|
+
# The generator target will be immediately evaluated, but the other
|
|
152
|
+
# elements will not be evaluated at the point of defining the GenExp.
|
|
153
|
+
# To consume those needs an explicit syntactic checkpoint
|
|
154
|
+
def visit_GeneratorExp(self, node: cst.GeneratorExp):
|
|
155
|
+
node.for_in.iter.visit(self)
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
|
|
66
159
|
@dataclass
|
|
67
160
|
class LoopState:
|
|
68
161
|
infinite_loop: bool = False
|
|
@@ -261,10 +354,10 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
261
354
|
"on line {1.lineno}."
|
|
262
355
|
),
|
|
263
356
|
"ASYNC912": (
|
|
264
|
-
"CancelScope with no guaranteed
|
|
357
|
+
"CancelScope with no guaranteed cancel point. This makes it potentially "
|
|
265
358
|
"impossible to cancel."
|
|
266
359
|
),
|
|
267
|
-
"ASYNC913":
|
|
360
|
+
"ASYNC913": "Indefinite loop with no guaranteed cancel points.",
|
|
268
361
|
"ASYNC100": (
|
|
269
362
|
"{0}.{1} context contains no checkpoints, remove the context or add"
|
|
270
363
|
" `await {0}.lowlevel.checkpoint()`."
|
|
@@ -274,7 +367,6 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
274
367
|
def __init__(self, *args: Any, **kwargs: Any):
|
|
275
368
|
super().__init__(*args, **kwargs)
|
|
276
369
|
self.has_yield = False
|
|
277
|
-
self.safe_decorator = False
|
|
278
370
|
self.async_function = False
|
|
279
371
|
self.uncheckpointed_statements: set[Statement] = set()
|
|
280
372
|
self.comp_unknown = False
|
|
@@ -285,10 +377,14 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
285
377
|
# ASYNC100
|
|
286
378
|
self.has_checkpoint_stack: list[bool] = []
|
|
287
379
|
self.node_dict: dict[cst.With, list[AttributeCall]] = {}
|
|
380
|
+
self.taskgroup_has_start_soon: dict[str, bool] = {}
|
|
288
381
|
|
|
289
382
|
# --exception-suppress-context-manager
|
|
290
383
|
self.suppress_imported_as: list[str] = []
|
|
291
384
|
|
|
385
|
+
# used to transfer new body between visit_FunctionDef and leave_FunctionDef
|
|
386
|
+
self.new_body: cst.BaseSuite | None = None
|
|
387
|
+
|
|
292
388
|
def should_autofix(self, node: cst.CSTNode, code: str | None = None) -> bool:
|
|
293
389
|
if code is None: # pragma: no branch
|
|
294
390
|
code = "ASYNC911" if self.has_yield else "ASYNC910"
|
|
@@ -299,13 +395,37 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
299
395
|
and self.library != ("asyncio",)
|
|
300
396
|
)
|
|
301
397
|
|
|
398
|
+
def checkpoint_cancel_point(self) -> None:
|
|
399
|
+
self.has_checkpoint_stack = [True] * len(self.has_checkpoint_stack)
|
|
400
|
+
# don't need to look for any .start_soon() calls
|
|
401
|
+
self.taskgroup_has_start_soon.clear()
|
|
402
|
+
|
|
403
|
+
def checkpoint_schedule_point(self) -> None:
|
|
404
|
+
# ASYNC912&ASYNC913 only cares about cancel points, so don't remove
|
|
405
|
+
# them if we only do a schedule point
|
|
406
|
+
self.uncheckpointed_statements = {
|
|
407
|
+
s
|
|
408
|
+
for s in self.uncheckpointed_statements
|
|
409
|
+
if isinstance(s, ArtificialStatement)
|
|
410
|
+
}
|
|
411
|
+
|
|
302
412
|
def checkpoint(self) -> None:
|
|
303
413
|
self.uncheckpointed_statements = set()
|
|
304
|
-
self.
|
|
414
|
+
self.checkpoint_cancel_point()
|
|
305
415
|
|
|
306
416
|
def checkpoint_statement(self) -> cst.SimpleStatementLine:
|
|
307
417
|
return checkpoint_statement(self.library[0])
|
|
308
418
|
|
|
419
|
+
def visit_Call(self, node: cst.Call) -> None:
|
|
420
|
+
# [Nursery/TaskGroup].start_soon introduces a cancel point
|
|
421
|
+
if (
|
|
422
|
+
isinstance(node.func, cst.Attribute)
|
|
423
|
+
and isinstance(node.func.value, cst.Name)
|
|
424
|
+
and node.func.attr.value == "start_soon"
|
|
425
|
+
and node.func.value.value in self.taskgroup_has_start_soon
|
|
426
|
+
):
|
|
427
|
+
self.taskgroup_has_start_soon[node.func.value.value] = True
|
|
428
|
+
|
|
309
429
|
def visit_ImportFrom(self, node: cst.ImportFrom) -> None:
|
|
310
430
|
# Semi-crude approach to handle `from contextlib import suppress`.
|
|
311
431
|
# It does not handle the identifier being overridden, or assigned
|
|
@@ -329,6 +449,10 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
329
449
|
return
|
|
330
450
|
|
|
331
451
|
def visit_FunctionDef(self, node: cst.FunctionDef) -> bool:
|
|
452
|
+
# `await` in default values happen in parent scope
|
|
453
|
+
# we also know we don't ever modify parameters so we can ignore the return value
|
|
454
|
+
_ = node.params.visit(self)
|
|
455
|
+
|
|
332
456
|
# don't lint functions whose bodies solely consist of pass or ellipsis
|
|
333
457
|
# @overload functions are also guaranteed to be empty
|
|
334
458
|
# we also ignore pytest fixtures
|
|
@@ -338,19 +462,23 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
338
462
|
self.save_state(
|
|
339
463
|
node,
|
|
340
464
|
"has_yield",
|
|
341
|
-
"safe_decorator",
|
|
342
465
|
"async_function",
|
|
343
466
|
"uncheckpointed_statements",
|
|
467
|
+
# comp_unknown does not need to be saved
|
|
344
468
|
"loop_state",
|
|
345
469
|
"try_state",
|
|
346
470
|
"has_checkpoint_stack",
|
|
347
|
-
|
|
471
|
+
# node_dict is cleaned up and don't need to be saved
|
|
472
|
+
"taskgroup_has_start_soon",
|
|
473
|
+
"suppress_imported_as", # a copy is saved, but state is not reset
|
|
348
474
|
copy=True,
|
|
349
475
|
)
|
|
350
476
|
self.uncheckpointed_statements = set()
|
|
351
477
|
self.has_checkpoint_stack = []
|
|
352
|
-
self.has_yield =
|
|
478
|
+
self.has_yield = False
|
|
353
479
|
self.loop_state = LoopState()
|
|
480
|
+
# try_state is reset upon entering try
|
|
481
|
+
self.taskgroup_has_start_soon = {}
|
|
354
482
|
|
|
355
483
|
self.async_function = (
|
|
356
484
|
node.asynchronous is not None
|
|
@@ -358,36 +486,49 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
358
486
|
node.decorators, *self.options.no_checkpoint_warning_decorators
|
|
359
487
|
)
|
|
360
488
|
)
|
|
361
|
-
if
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
489
|
+
# only visit subnodes if there is an async function defined inside
|
|
490
|
+
# this should improve performance on codebases with many sync functions
|
|
491
|
+
if not self.async_function and not any(
|
|
492
|
+
m.findall(node, m.FunctionDef(asynchronous=m.Asynchronous()))
|
|
493
|
+
):
|
|
494
|
+
return False
|
|
365
495
|
|
|
366
496
|
pos = self.get_metadata(PositionProvider, node).start # type: ignore
|
|
367
497
|
self.uncheckpointed_statements = {
|
|
368
498
|
Statement("function definition", pos.line, pos.column) # type: ignore
|
|
369
499
|
}
|
|
370
|
-
|
|
500
|
+
|
|
501
|
+
# visit body
|
|
502
|
+
# we're not gonna get FlattenSentinel or RemovalSentinel
|
|
503
|
+
self.new_body = cast(cst.BaseSuite, node.body.visit(self))
|
|
504
|
+
|
|
505
|
+
# we know that leave_FunctionDef for this FunctionDef will run immediately after
|
|
506
|
+
# this function exits so we don't need to worry about save_state for new_body
|
|
507
|
+
return False
|
|
371
508
|
|
|
372
509
|
def leave_FunctionDef(
|
|
373
510
|
self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef
|
|
374
511
|
) -> cst.FunctionDef:
|
|
375
512
|
if (
|
|
376
|
-
self.
|
|
513
|
+
self.new_body is not None
|
|
514
|
+
and self.async_function
|
|
377
515
|
# updated_node does not have a position, so we must send original_node
|
|
378
516
|
and self.check_function_exit(original_node)
|
|
379
517
|
and self.should_autofix(original_node)
|
|
380
|
-
and isinstance(
|
|
518
|
+
and isinstance(self.new_body, cst.IndentedBlock)
|
|
381
519
|
):
|
|
382
520
|
# insert checkpoint at the end of body
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
updated_node = updated_node.with_changes(body=indentedblock)
|
|
521
|
+
new_body_block = list(self.new_body.body)
|
|
522
|
+
new_body_block.append(self.checkpoint_statement())
|
|
523
|
+
self.new_body = self.new_body.with_changes(body=new_body_block)
|
|
387
524
|
|
|
388
525
|
self.ensure_imported_library()
|
|
389
526
|
|
|
527
|
+
if self.new_body is not None:
|
|
528
|
+
updated_node = updated_node.with_changes(body=self.new_body)
|
|
390
529
|
self.restore_state(original_node)
|
|
530
|
+
# reset self.new_body
|
|
531
|
+
self.new_body = None
|
|
391
532
|
return updated_node
|
|
392
533
|
|
|
393
534
|
# error if function exit/return/yields with uncheckpointed statements
|
|
@@ -440,8 +581,8 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
440
581
|
):
|
|
441
582
|
self.add_statement = self.checkpoint_statement()
|
|
442
583
|
# avoid duplicate error messages
|
|
443
|
-
|
|
444
|
-
|
|
584
|
+
# but don't see it as a cancel point for ASYNC100
|
|
585
|
+
self.checkpoint_schedule_point()
|
|
445
586
|
|
|
446
587
|
# return original node to avoid problems with identity equality
|
|
447
588
|
assert original_node.deep_equals(updated_node)
|
|
@@ -491,12 +632,47 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
491
632
|
is not None
|
|
492
633
|
)
|
|
493
634
|
|
|
635
|
+
def _checkpoint_with(self, node: cst.With, entry: bool):
|
|
636
|
+
"""Conditionally checkpoints entry/exit of With.
|
|
637
|
+
|
|
638
|
+
If the `with` only contains calls to open_nursery/create_task_group, it's a
|
|
639
|
+
schedule point but not a cancellation point, so we treat it as a checkpoint
|
|
640
|
+
for async91x but not for async100.
|
|
641
|
+
|
|
642
|
+
Saves the name of the taskgroup/nursery if entry is set
|
|
643
|
+
"""
|
|
644
|
+
if not getattr(node, "asynchronous", None):
|
|
645
|
+
return
|
|
646
|
+
|
|
647
|
+
for item in node.items:
|
|
648
|
+
if isinstance(item.item, cst.Call) and identifier_to_string(
|
|
649
|
+
item.item.func
|
|
650
|
+
) in (
|
|
651
|
+
"trio.open_nursery",
|
|
652
|
+
"anyio.create_task_group",
|
|
653
|
+
):
|
|
654
|
+
if item.asname is not None and isinstance(item.asname.name, cst.Name):
|
|
655
|
+
# save the nursery/taskgroup to see if it has a `.start_soon`
|
|
656
|
+
if entry:
|
|
657
|
+
self.taskgroup_has_start_soon[item.asname.name.value] = False
|
|
658
|
+
elif self.taskgroup_has_start_soon.pop(
|
|
659
|
+
item.asname.name.value, False
|
|
660
|
+
):
|
|
661
|
+
self.checkpoint()
|
|
662
|
+
return
|
|
663
|
+
else:
|
|
664
|
+
self.checkpoint()
|
|
665
|
+
break
|
|
666
|
+
else:
|
|
667
|
+
# only taskgroup/nursery calls
|
|
668
|
+
self.checkpoint_schedule_point()
|
|
669
|
+
|
|
494
670
|
# Async context managers can reasonably checkpoint on either or both of entry and
|
|
495
671
|
# exit. Given that we can't tell which, we assume "both" to avoid raising a
|
|
496
672
|
# missing-checkpoint warning when there might in fact be one (i.e. a false alarm).
|
|
497
673
|
def visit_With_body(self, node: cst.With):
|
|
498
|
-
|
|
499
|
-
|
|
674
|
+
self.save_state(node, "taskgroup_has_start_soon", copy=True)
|
|
675
|
+
self._checkpoint_with(node, entry=True)
|
|
500
676
|
|
|
501
677
|
# if this might suppress exceptions, we cannot treat anything inside it as
|
|
502
678
|
# checkpointing.
|
|
@@ -548,6 +724,8 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
548
724
|
for res in self.node_dict[original_node]:
|
|
549
725
|
self.error(res.node, error_code="ASYNC912")
|
|
550
726
|
|
|
727
|
+
self.node_dict.pop(original_node, None)
|
|
728
|
+
|
|
551
729
|
# if exception-suppressing, restore all uncheckpointed statements from
|
|
552
730
|
# before the `with`.
|
|
553
731
|
if self._is_exception_suppressing_context_manager(original_node):
|
|
@@ -555,8 +733,7 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
555
733
|
self.restore_state(original_node)
|
|
556
734
|
self.uncheckpointed_statements.update(prev_checkpoints)
|
|
557
735
|
|
|
558
|
-
|
|
559
|
-
self.checkpoint()
|
|
736
|
+
self._checkpoint_with(original_node, entry=False)
|
|
560
737
|
return updated_node
|
|
561
738
|
|
|
562
739
|
# error if no checkpoint since earlier yield or function entry
|
|
@@ -569,7 +746,7 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
569
746
|
|
|
570
747
|
# Treat as a checkpoint for ASYNC100, since the context we yield to
|
|
571
748
|
# may checkpoint.
|
|
572
|
-
self.
|
|
749
|
+
self.checkpoint_cancel_point()
|
|
573
750
|
|
|
574
751
|
if self.check_function_exit(original_node) and self.should_autofix(
|
|
575
752
|
original_node
|
|
@@ -978,8 +1155,8 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
978
1155
|
return False
|
|
979
1156
|
|
|
980
1157
|
# The generator target will be immediately evaluated, but the other
|
|
981
|
-
# elements will be
|
|
982
|
-
#
|
|
1158
|
+
# elements will not be evaluated at the point of defining the GenExp.
|
|
1159
|
+
# To consume those needs an explicit syntactic checkpoint
|
|
983
1160
|
def visit_GeneratorExp(self, node: cst.GeneratorExp):
|
|
984
1161
|
node.for_in.iter.visit(self)
|
|
985
1162
|
return False
|
|
@@ -121,10 +121,10 @@ class Visitor112(Flake8AsyncVisitor):
|
|
|
121
121
|
elif get_matching_call(
|
|
122
122
|
item.context_expr, "create_task_group", base="anyio"
|
|
123
123
|
):
|
|
124
|
-
nursery_type = "
|
|
124
|
+
nursery_type = "task group"
|
|
125
125
|
# check for asyncio.TaskGroup
|
|
126
126
|
elif get_matching_call(item.context_expr, "TaskGroup", base="asyncio"):
|
|
127
|
-
nursery_type = "
|
|
127
|
+
nursery_type = "task group"
|
|
128
128
|
start_methods = ("create_task",)
|
|
129
129
|
else:
|
|
130
130
|
# incorrectly marked as not covered on py39
|
|
@@ -151,12 +151,12 @@ class Visitor112(Flake8AsyncVisitor):
|
|
|
151
151
|
|
|
152
152
|
|
|
153
153
|
# used in 113 and 114
|
|
154
|
-
STARTABLE_CALLS = (
|
|
154
|
+
STARTABLE_CALLS = ("serve",)
|
|
155
|
+
TRIO_STARTABLE_CALLS = (
|
|
155
156
|
"run_process",
|
|
156
157
|
"serve_ssl_over_tcp",
|
|
157
158
|
"serve_tcp",
|
|
158
159
|
"serve_listeners",
|
|
159
|
-
"serve",
|
|
160
160
|
)
|
|
161
161
|
|
|
162
162
|
|
|
@@ -201,7 +201,11 @@ class Visitor113(Flake8AsyncVisitor):
|
|
|
201
201
|
if isinstance(n, ast.Name):
|
|
202
202
|
return n.id in startable_list
|
|
203
203
|
if isinstance(n, ast.Attribute):
|
|
204
|
-
return n.attr in startable_list
|
|
204
|
+
return n.attr in startable_list and not (
|
|
205
|
+
n.attr in TRIO_STARTABLE_CALLS
|
|
206
|
+
and isinstance(n.value, ast.Name)
|
|
207
|
+
and n.value.id != "trio"
|
|
208
|
+
)
|
|
205
209
|
if isinstance(n, ast.Call):
|
|
206
210
|
return any(is_startable(nn, *startable_list) for nn in n.args)
|
|
207
211
|
return False
|
|
@@ -213,12 +217,16 @@ class Visitor113(Flake8AsyncVisitor):
|
|
|
213
217
|
):
|
|
214
218
|
return False
|
|
215
219
|
var = ast.unparse(node.value)
|
|
216
|
-
return (
|
|
217
|
-
self.
|
|
218
|
-
in (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
return (
|
|
221
|
+
("trio" in self.library and var.endswith("nursery"))
|
|
222
|
+
or ("anyio" in self.library and var.endswith("task_group"))
|
|
223
|
+
or (
|
|
224
|
+
self.variables.get(var, "")
|
|
225
|
+
in (
|
|
226
|
+
"trio.Nursery",
|
|
227
|
+
"anyio.TaskGroup",
|
|
228
|
+
"asyncio.TaskGroup",
|
|
229
|
+
)
|
|
222
230
|
)
|
|
223
231
|
)
|
|
224
232
|
|
|
@@ -229,6 +237,7 @@ class Visitor113(Flake8AsyncVisitor):
|
|
|
229
237
|
and is_startable(
|
|
230
238
|
node.args[0],
|
|
231
239
|
*STARTABLE_CALLS,
|
|
240
|
+
*TRIO_STARTABLE_CALLS,
|
|
232
241
|
*self.options.startable_in_context_manager,
|
|
233
242
|
)
|
|
234
243
|
):
|
|
@@ -254,7 +263,11 @@ class Visitor114(Flake8AsyncVisitor):
|
|
|
254
263
|
for n in self.walk(*node.args.args, *node.args.kwonlyargs)
|
|
255
264
|
) and not any(
|
|
256
265
|
node.name == opt
|
|
257
|
-
for opt in (
|
|
266
|
+
for opt in (
|
|
267
|
+
*self.options.startable_in_context_manager,
|
|
268
|
+
*STARTABLE_CALLS,
|
|
269
|
+
*TRIO_STARTABLE_CALLS,
|
|
270
|
+
)
|
|
258
271
|
):
|
|
259
272
|
self.error(node, node.name)
|
|
260
273
|
|
|
@@ -319,8 +332,10 @@ class Visitor116(Flake8AsyncVisitor):
|
|
|
319
332
|
@error_class
|
|
320
333
|
class Visitor119(Flake8AsyncVisitor):
|
|
321
334
|
error_codes: Mapping[str, str] = {
|
|
322
|
-
"ASYNC119":
|
|
323
|
-
|
|
335
|
+
"ASYNC119": (
|
|
336
|
+
"Yield in contextmanager in async generator might not trigger"
|
|
337
|
+
" cleanup. Use `@asynccontextmanager` or refactor."
|
|
338
|
+
)
|
|
324
339
|
}
|
|
325
340
|
|
|
326
341
|
def __init__(self, *args: Any, **kwargs: Any):
|
|
@@ -490,8 +505,10 @@ class Visitor300(Flake8AsyncVisitor_cst):
|
|
|
490
505
|
@disabled_by_default
|
|
491
506
|
class Visitor900(Flake8AsyncVisitor):
|
|
492
507
|
error_codes: Mapping[str, str] = {
|
|
493
|
-
"ASYNC900":
|
|
494
|
-
|
|
508
|
+
"ASYNC900": (
|
|
509
|
+
"Async generator not allowed, unless transformed "
|
|
510
|
+
"by a known decorator (one of: {})."
|
|
511
|
+
)
|
|
495
512
|
}
|
|
496
513
|
|
|
497
514
|
def __init__(self, *args: Any, **kwargs: Any):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version:
|
|
3
|
+
Version: 25.2.2
|
|
4
4
|
Summary: A highly opinionated flake8 plugin for Trio-related problems.
|
|
5
5
|
Author: Zac Hatfield-Dodds, John Litborn, and Contributors
|
|
6
6
|
Author-email: zac@zhd.dev
|
|
@@ -21,9 +21,18 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Requires-Python: >=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
|
-
License-File: LICENSE
|
|
25
24
|
Requires-Dist: flake8>=6
|
|
26
25
|
Requires-Dist: libcst>=1.0.1
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: license
|
|
32
|
+
Dynamic: project-url
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
27
36
|
|
|
28
37
|
[](https://flake8-async.readthedocs.io)
|
|
29
38
|
[](https://pypi.org/project/flake8-async)
|
|
@@ -32,9 +32,9 @@ flake8_async/visitors/visitor91x.py
|
|
|
32
32
|
flake8_async/visitors/visitor_utility.py
|
|
33
33
|
flake8_async/visitors/visitors.py
|
|
34
34
|
tests/__init__.py
|
|
35
|
+
tests/check_changelog_and_version.py
|
|
35
36
|
tests/conftest.py
|
|
36
37
|
tests/test_all_visitors_imported.py
|
|
37
|
-
tests/test_changelog_and_version.py
|
|
38
38
|
tests/test_config_and_args.py
|
|
39
39
|
tests/test_decorator.py
|
|
40
40
|
tests/test_exception_on_invalid_code.py
|
|
@@ -45,12 +45,13 @@ tests/trio_options.py
|
|
|
45
45
|
tests/autofix_files/async100.py
|
|
46
46
|
tests/autofix_files/async100_asyncio.py
|
|
47
47
|
tests/autofix_files/async100_simple_autofix.py
|
|
48
|
-
tests/autofix_files/
|
|
48
|
+
tests/autofix_files/async124.py
|
|
49
49
|
tests/autofix_files/async910.py
|
|
50
50
|
tests/autofix_files/async910_insert_library.py
|
|
51
51
|
tests/autofix_files/async911.py
|
|
52
52
|
tests/autofix_files/async911_insert_library.py
|
|
53
53
|
tests/autofix_files/async913.py
|
|
54
|
+
tests/autofix_files/async913_trio_anyio.py
|
|
54
55
|
tests/autofix_files/async91x_autofix.py
|
|
55
56
|
tests/autofix_files/exception_suppress_context_manager.py
|
|
56
57
|
tests/autofix_files/exception_suppress_context_manager_import_star.py
|
|
@@ -61,11 +62,8 @@ tests/eval_files/async100.py
|
|
|
61
62
|
tests/eval_files/async100_asyncio.py
|
|
62
63
|
tests/eval_files/async100_noautofix.py
|
|
63
64
|
tests/eval_files/async100_simple_autofix.py
|
|
64
|
-
tests/eval_files/async100_trio.py
|
|
65
65
|
tests/eval_files/async101.py
|
|
66
|
-
tests/eval_files/async101_anyio.py
|
|
67
66
|
tests/eval_files/async101_asyncio.py
|
|
68
|
-
tests/eval_files/async101_trio.py
|
|
69
67
|
tests/eval_files/async102.py
|
|
70
68
|
tests/eval_files/async102_aclose.py
|
|
71
69
|
tests/eval_files/async102_aclose_args.py
|
|
@@ -86,13 +84,10 @@ tests/eval_files/async106.py
|
|
|
86
84
|
tests/eval_files/async109.py
|
|
87
85
|
tests/eval_files/async110.py
|
|
88
86
|
tests/eval_files/async111.py
|
|
89
|
-
tests/eval_files/async111_anyio.py
|
|
90
87
|
tests/eval_files/async111_asyncio.py
|
|
91
88
|
tests/eval_files/async112.py
|
|
92
|
-
tests/eval_files/async112_anyio.py
|
|
93
89
|
tests/eval_files/async112_asyncio.py
|
|
94
90
|
tests/eval_files/async113.py
|
|
95
|
-
tests/eval_files/async113_anyio.py
|
|
96
91
|
tests/eval_files/async113_asyncio.py
|
|
97
92
|
tests/eval_files/async113_trio.py
|
|
98
93
|
tests/eval_files/async114.py
|
|
@@ -107,6 +102,8 @@ tests/eval_files/async121_asyncio.py
|
|
|
107
102
|
tests/eval_files/async122.py
|
|
108
103
|
tests/eval_files/async123.py
|
|
109
104
|
tests/eval_files/async123_py311.py
|
|
105
|
+
tests/eval_files/async124.py
|
|
106
|
+
tests/eval_files/async124_no_autofix.py
|
|
110
107
|
tests/eval_files/async200.py
|
|
111
108
|
tests/eval_files/async210.py
|
|
112
109
|
tests/eval_files/async211.py
|
|
@@ -131,6 +128,7 @@ tests/eval_files/async911_insert_library.py
|
|
|
131
128
|
tests/eval_files/async912.py
|
|
132
129
|
tests/eval_files/async912_asyncio.py
|
|
133
130
|
tests/eval_files/async913.py
|
|
131
|
+
tests/eval_files/async913_trio_anyio.py
|
|
134
132
|
tests/eval_files/async91x_autofix.py
|
|
135
133
|
tests/eval_files/async91x_noautofix.py
|
|
136
134
|
tests/eval_files/exception_suppress_context_manager.py
|