flake8-async 25.2.2__tar.gz → 25.3.1__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-25.2.2 → flake8_async-25.3.1}/PKG-INFO +4 -2
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/__init__.py +41 -7
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/helpers.py +1 -1
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor102_120.py +3 -1
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor103_104.py +3 -1
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor123.py +1 -2
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor91x.py +25 -11
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async.egg-info/PKG-INFO +4 -2
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async.egg-info/SOURCES.txt +5 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async.egg-info/requires.txt +3 -1
- {flake8_async-25.2.2 → flake8_async-25.3.1}/pyproject.toml +32 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/setup.py +2 -1
- flake8_async-25.3.1/tests/autofix_files/async91x_py311.py +97 -0
- flake8_async-25.3.1/tests/eval_files/async102_120_py311.py +29 -0
- flake8_async-25.3.1/tests/eval_files/async103_104_py311.py +40 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async119.py +1 -1
- flake8_async-25.3.1/tests/eval_files/async912_py311.py +16 -0
- flake8_async-25.3.1/tests/eval_files/async91x_py311.py +92 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/test_config_and_args.py +58 -3
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/test_decorator.py +24 -9
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/test_flake8_async.py +5 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tox.ini +5 -29
- {flake8_async-25.2.2 → flake8_async-25.3.1}/CHANGELOG.md +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/CONTRIBUTING.md +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/LICENSE +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/MANIFEST.in +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/README.md +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/__main__.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/base.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/runner.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/__init__.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/flake8asyncvisitor.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor101.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor105.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor111.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor118.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor2xx.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitor_utility.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async/visitors/visitors.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async.egg-info/dependency_links.txt +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async.egg-info/entry_points.txt +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async.egg-info/not-zip-safe +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/flake8_async.egg-info/top_level.txt +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/setup.cfg +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/__init__.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async100.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async100_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async100_simple_autofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async124.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async910.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async910_insert_library.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async911.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async911_insert_library.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async913.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async913_trio_anyio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/async91x_autofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/exception_suppress_context_manager.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/exception_suppress_context_manager_import_star.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/noqa.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/autofix_files/noqa_testing.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/check_changelog_and_version.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/conftest.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/anyio_trio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async100.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async100_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async100_noautofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async100_simple_autofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async101.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async101_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async102.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async102_aclose.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async102_aclose_args.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async102_anyio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async102_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async102_trio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async103.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async103_all_imported.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async103_both_imported.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async103_no_104.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async103_trio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async104.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async104_anyio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async104_trio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async105.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async105_anyio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async106.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async109.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async110.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async111.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async111_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async112.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async112_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async113.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async113_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async113_trio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async114.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async115.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async116.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async118.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async120.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async121.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async121_anyio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async121_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async122.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async123.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async123_py311.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async124.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async124_no_autofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async200.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async210.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async211.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async212.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async22x.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async22x_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async232.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async232_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async23x.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async23x_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async240.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async250.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async250_multi_library.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async251.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async251_multi_library.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async300.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async900.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async910.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async910_insert_library.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async911.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async911_insert_library.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async912.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async912_asyncio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async913.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async913_trio_anyio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async91x_autofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/async91x_noautofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/exception_suppress_context_manager.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/exception_suppress_context_manager_import_star.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/no_library.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/noqa.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/noqa_no_autofix.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/noqa_testing.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/eval_files/trio_anyio.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/test_all_visitors_imported.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/test_exception_on_invalid_code.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/test_formatting.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/test_messages_documented.py +0 -0
- {flake8_async-25.2.2 → flake8_async-25.3.1}/tests/trio_options.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version: 25.
|
|
3
|
+
Version: 25.3.1
|
|
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,8 +21,9 @@ 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
|
-
Requires-Dist: flake8>=6
|
|
25
24
|
Requires-Dist: libcst>=1.0.1
|
|
25
|
+
Provides-Extra: flake8
|
|
26
|
+
Requires-Dist: flake8>=6; extra == "flake8"
|
|
26
27
|
Dynamic: author
|
|
27
28
|
Dynamic: author-email
|
|
28
29
|
Dynamic: classifier
|
|
@@ -30,6 +31,7 @@ Dynamic: description
|
|
|
30
31
|
Dynamic: description-content-type
|
|
31
32
|
Dynamic: license
|
|
32
33
|
Dynamic: project-url
|
|
34
|
+
Dynamic: provides-extra
|
|
33
35
|
Dynamic: requires-dist
|
|
34
36
|
Dynamic: requires-python
|
|
35
37
|
Dynamic: summary
|
|
@@ -38,7 +38,7 @@ if TYPE_CHECKING:
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
# CalVer: YY.month.patch, e.g. first release of July 2022 == "22.7.1"
|
|
41
|
-
__version__ = "25.
|
|
41
|
+
__version__ = "25.3.1"
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
# taken from https://github.com/Zac-HD/shed
|
|
@@ -127,26 +127,32 @@ class Plugin:
|
|
|
127
127
|
assert self._options is not None
|
|
128
128
|
return self._options
|
|
129
129
|
|
|
130
|
-
def __init__(
|
|
130
|
+
def __init__(
|
|
131
|
+
self, tree: ast.AST, lines: Sequence[str], filename: str | None = None
|
|
132
|
+
):
|
|
131
133
|
super().__init__()
|
|
134
|
+
self.filename: str | None = filename
|
|
132
135
|
self._tree = tree
|
|
133
136
|
source = "".join(lines)
|
|
134
137
|
|
|
135
138
|
self.module: cst.Module = cst_parse_module_native(source)
|
|
136
139
|
|
|
137
140
|
@classmethod
|
|
138
|
-
def from_filename(cls, filename: str | PathLike[str]) -> Plugin:
|
|
141
|
+
def from_filename(cls, filename: str | PathLike[str]) -> Plugin:
|
|
139
142
|
# only used with --runslow
|
|
140
143
|
with tokenize.open(filename) as f:
|
|
141
144
|
source = f.read()
|
|
142
|
-
return cls.from_source(source)
|
|
145
|
+
return cls.from_source(source, filename=filename)
|
|
143
146
|
|
|
144
147
|
# alternative `__init__` to avoid re-splitting and/or re-joining lines
|
|
145
148
|
@classmethod
|
|
146
|
-
def from_source(
|
|
149
|
+
def from_source(
|
|
150
|
+
cls, source: str, filename: str | PathLike[str] | None = None
|
|
151
|
+
) -> Plugin:
|
|
147
152
|
plugin = Plugin.__new__(cls)
|
|
148
153
|
super(Plugin, plugin).__init__()
|
|
149
154
|
plugin._tree = ast.parse(source)
|
|
155
|
+
plugin.filename = str(filename) if filename else None
|
|
150
156
|
plugin.module = cst_parse_module_native(source)
|
|
151
157
|
return plugin
|
|
152
158
|
|
|
@@ -198,7 +204,7 @@ class Plugin:
|
|
|
198
204
|
'lines with "# noqa" at the end.'
|
|
199
205
|
),
|
|
200
206
|
)
|
|
201
|
-
else: #
|
|
207
|
+
else: # pragma: no-cov-no-flake8
|
|
202
208
|
Plugin.standalone = False
|
|
203
209
|
# Disable ASYNC9xx calls by default
|
|
204
210
|
option_manager.extend_default_ignore(default_disabled_error_codes)
|
|
@@ -231,6 +237,13 @@ class Plugin:
|
|
|
231
237
|
" errors."
|
|
232
238
|
),
|
|
233
239
|
)
|
|
240
|
+
add_argument(
|
|
241
|
+
"--per-file-disable",
|
|
242
|
+
type=parse_per_file_disable,
|
|
243
|
+
default={},
|
|
244
|
+
required=False,
|
|
245
|
+
help=("..."),
|
|
246
|
+
)
|
|
234
247
|
add_argument(
|
|
235
248
|
"--autofix",
|
|
236
249
|
type=comma_separated_list,
|
|
@@ -367,7 +380,7 @@ class Plugin:
|
|
|
367
380
|
}
|
|
368
381
|
assert all_codes
|
|
369
382
|
|
|
370
|
-
if options.autofix and not Plugin.standalone:
|
|
383
|
+
if options.autofix and not Plugin.standalone: # pragma: no-cov-no-flake8
|
|
371
384
|
print("Cannot autofix when run as a flake8 plugin.", file=sys.stderr)
|
|
372
385
|
sys.exit(1)
|
|
373
386
|
autofix_codes = set(get_matching_codes(options.autofix, all_codes))
|
|
@@ -441,3 +454,24 @@ def parse_async200_dict(raw_value: str) -> dict[str, str]:
|
|
|
441
454
|
)
|
|
442
455
|
res[split_values[0]] = split_values[1]
|
|
443
456
|
return res
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
# not run if flake8 is installed
|
|
460
|
+
# TODO: this is not tested at all atm, I'm not even sure if it works
|
|
461
|
+
def parse_per_file_disable( # pragma: no cover
|
|
462
|
+
raw_value: str,
|
|
463
|
+
) -> dict[str, tuple[str, ...]]:
|
|
464
|
+
res: dict[str, tuple[str, ...]] = {}
|
|
465
|
+
splitter = "->"
|
|
466
|
+
values = [s.strip() for s in raw_value.split(" \t\n") if s.strip()]
|
|
467
|
+
for value in values:
|
|
468
|
+
split_values = list(map(str.strip, value.split(splitter)))
|
|
469
|
+
if len(split_values) != 2:
|
|
470
|
+
# argparse will eat this error message and spit out its own
|
|
471
|
+
# if we raise it as ValueError
|
|
472
|
+
raise ArgumentTypeError(
|
|
473
|
+
f"Invalid number ({len(split_values)-1}) of splitter "
|
|
474
|
+
f"tokens {splitter!r} in {value!r}"
|
|
475
|
+
)
|
|
476
|
+
res[split_values[0]] = tuple(split_values[1].split(","))
|
|
477
|
+
return res
|
|
@@ -149,7 +149,7 @@ class Visitor102(Flake8AsyncVisitor):
|
|
|
149
149
|
break
|
|
150
150
|
self.visit_With(node)
|
|
151
151
|
|
|
152
|
-
def visit_Try(self, node: ast.Try):
|
|
152
|
+
def visit_Try(self, node: ast.Try | ast.TryStar): # type: ignore[name-defined]
|
|
153
153
|
self.save_state(
|
|
154
154
|
node, "_critical_scope", "_trio_context_managers", "cancelled_caught"
|
|
155
155
|
)
|
|
@@ -165,6 +165,8 @@ class Visitor102(Flake8AsyncVisitor):
|
|
|
165
165
|
self._critical_scope = Statement("try/finally", node.lineno, node.col_offset)
|
|
166
166
|
self.visit_nodes(node.finalbody)
|
|
167
167
|
|
|
168
|
+
visit_TryStar = visit_Try
|
|
169
|
+
|
|
168
170
|
def visit_ExceptHandler(self, node: ast.ExceptHandler):
|
|
169
171
|
# if we're inside a critical scope, a nested except should never override that
|
|
170
172
|
if self._critical_scope is not None and self._critical_scope.name != "except":
|
|
@@ -160,7 +160,7 @@ class Visitor103_104(Flake8AsyncVisitor):
|
|
|
160
160
|
visit_Yield = visit_Return
|
|
161
161
|
|
|
162
162
|
# Treat Try's as fully covering only if `finally` always raises.
|
|
163
|
-
def visit_Try(self, node: ast.Try):
|
|
163
|
+
def visit_Try(self, node: ast.Try | ast.TryStar): # type: ignore[name-defined]
|
|
164
164
|
self.save_state(node, "cancelled_caught", copy=True)
|
|
165
165
|
self.cancelled_caught = set()
|
|
166
166
|
|
|
@@ -179,6 +179,8 @@ class Visitor103_104(Flake8AsyncVisitor):
|
|
|
179
179
|
# but it's fine if we raise in finally
|
|
180
180
|
self.visit_nodes(node.finalbody)
|
|
181
181
|
|
|
182
|
+
visit_TryStar = visit_Try
|
|
183
|
+
|
|
182
184
|
# Treat if's as fully covering if both `if` and `else` raise.
|
|
183
185
|
# `elif` is parsed by the ast as a new if statement inside the else.
|
|
184
186
|
def visit_If(self, node: ast.If):
|
|
@@ -78,8 +78,7 @@ class Visitor123(Flake8AsyncVisitor):
|
|
|
78
78
|
self.exception_group_names = {node.name}
|
|
79
79
|
|
|
80
80
|
# ast.TryStar added in py311
|
|
81
|
-
|
|
82
|
-
def visit_TryStar(self, node: ast.TryStar): # type: ignore[name-defined] # pragma: no cover
|
|
81
|
+
def visit_TryStar(self, node: ast.TryStar): # type: ignore[name-defined] # pragma: no-cov-py-lt-311
|
|
83
82
|
self.save_state(node, "try_star", copy=False)
|
|
84
83
|
self.try_star = True
|
|
85
84
|
|
|
@@ -386,7 +386,7 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
386
386
|
self.new_body: cst.BaseSuite | None = None
|
|
387
387
|
|
|
388
388
|
def should_autofix(self, node: cst.CSTNode, code: str | None = None) -> bool:
|
|
389
|
-
if code is None:
|
|
389
|
+
if code is None:
|
|
390
390
|
code = "ASYNC911" if self.has_yield else "ASYNC910"
|
|
391
391
|
|
|
392
392
|
return (
|
|
@@ -768,7 +768,7 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
768
768
|
# try can jump into any except or into the finally* at any point during it's
|
|
769
769
|
# execution so we need to make sure except & finally can handle worst-case
|
|
770
770
|
# * unless there's a bare except / except BaseException - not implemented.
|
|
771
|
-
def visit_Try(self, node: cst.Try):
|
|
771
|
+
def visit_Try(self, node: cst.Try | cst.TryStar):
|
|
772
772
|
if not self.async_function:
|
|
773
773
|
return
|
|
774
774
|
self.save_state(node, "try_state", copy=True)
|
|
@@ -784,39 +784,41 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
784
784
|
Statement("yield", pos.line, pos.column) # type: ignore
|
|
785
785
|
)
|
|
786
786
|
|
|
787
|
-
def leave_Try_body(self, node: cst.Try):
|
|
787
|
+
def leave_Try_body(self, node: cst.Try | cst.TryStar):
|
|
788
788
|
# save state at end of try for entering else
|
|
789
789
|
self.try_state.try_checkpoint = self.uncheckpointed_statements
|
|
790
790
|
|
|
791
791
|
# check that all except handlers checkpoint (await or most likely raise)
|
|
792
792
|
self.try_state.except_uncheckpointed_statements = set()
|
|
793
793
|
|
|
794
|
-
def visit_ExceptHandler(self, node: cst.ExceptHandler):
|
|
794
|
+
def visit_ExceptHandler(self, node: cst.ExceptHandler | cst.ExceptStarHandler):
|
|
795
795
|
# enter with worst case of try
|
|
796
796
|
self.uncheckpointed_statements = (
|
|
797
797
|
self.try_state.body_uncheckpointed_statements.copy()
|
|
798
798
|
)
|
|
799
799
|
|
|
800
800
|
def leave_ExceptHandler(
|
|
801
|
-
self,
|
|
802
|
-
|
|
801
|
+
self,
|
|
802
|
+
original_node: cst.ExceptHandler | cst.ExceptStarHandler,
|
|
803
|
+
updated_node: cst.ExceptHandler | cst.ExceptStarHandler,
|
|
804
|
+
) -> Any: # not worth creating a TypeVar to handle correctly
|
|
803
805
|
self.try_state.except_uncheckpointed_statements.update(
|
|
804
806
|
self.uncheckpointed_statements
|
|
805
807
|
)
|
|
806
808
|
return updated_node
|
|
807
809
|
|
|
808
|
-
def visit_Try_orelse(self, node: cst.Try):
|
|
810
|
+
def visit_Try_orelse(self, node: cst.Try | cst.TryStar):
|
|
809
811
|
# check else
|
|
810
812
|
# if else runs it's after all of try, so restore state to back then
|
|
811
813
|
self.uncheckpointed_statements = self.try_state.try_checkpoint
|
|
812
814
|
|
|
813
|
-
def leave_Try_orelse(self, node: cst.Try):
|
|
815
|
+
def leave_Try_orelse(self, node: cst.Try | cst.TryStar):
|
|
814
816
|
# checkpoint if else checkpoints, and all excepts checkpoint
|
|
815
817
|
self.uncheckpointed_statements.update(
|
|
816
818
|
self.try_state.except_uncheckpointed_statements
|
|
817
819
|
)
|
|
818
820
|
|
|
819
|
-
def visit_Try_finalbody(self, node: cst.Try):
|
|
821
|
+
def visit_Try_finalbody(self, node: cst.Try | cst.TryStar):
|
|
820
822
|
if node.finalbody:
|
|
821
823
|
self.try_state.added = (
|
|
822
824
|
self.try_state.body_uncheckpointed_statements.difference(
|
|
@@ -835,14 +837,26 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
835
837
|
):
|
|
836
838
|
self.uncheckpointed_statements.update(self.try_state.added)
|
|
837
839
|
|
|
838
|
-
def leave_Try_finalbody(self, node: cst.Try):
|
|
840
|
+
def leave_Try_finalbody(self, node: cst.Try | cst.TryStar):
|
|
839
841
|
if node.finalbody:
|
|
840
842
|
self.uncheckpointed_statements.difference_update(self.try_state.added)
|
|
841
843
|
|
|
842
|
-
def leave_Try(
|
|
844
|
+
def leave_Try(
|
|
845
|
+
self, original_node: cst.Try | cst.TryStar, updated_node: cst.Try | cst.TryStar
|
|
846
|
+
) -> cst.Try | cst.TryStar:
|
|
843
847
|
self.restore_state(original_node)
|
|
844
848
|
return updated_node
|
|
845
849
|
|
|
850
|
+
visit_TryStar = visit_Try
|
|
851
|
+
leave_TryStar = leave_Try
|
|
852
|
+
leave_TryStar_body = leave_Try_body
|
|
853
|
+
visit_TryStar_orelse = visit_Try_orelse
|
|
854
|
+
leave_TryStar_orelse = leave_Try_orelse
|
|
855
|
+
visit_TryStar_finalbody = visit_Try_finalbody
|
|
856
|
+
leave_TryStar_finalbody = leave_Try_finalbody
|
|
857
|
+
visit_ExceptStarHandler = visit_ExceptHandler
|
|
858
|
+
leave_ExceptStarHandler = leave_ExceptHandler
|
|
859
|
+
|
|
846
860
|
def leave_If_test(self, node: cst.If | cst.IfExp) -> None:
|
|
847
861
|
if not self.async_function:
|
|
848
862
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version: 25.
|
|
3
|
+
Version: 25.3.1
|
|
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,8 +21,9 @@ 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
|
-
Requires-Dist: flake8>=6
|
|
25
24
|
Requires-Dist: libcst>=1.0.1
|
|
25
|
+
Provides-Extra: flake8
|
|
26
|
+
Requires-Dist: flake8>=6; extra == "flake8"
|
|
26
27
|
Dynamic: author
|
|
27
28
|
Dynamic: author-email
|
|
28
29
|
Dynamic: classifier
|
|
@@ -30,6 +31,7 @@ Dynamic: description
|
|
|
30
31
|
Dynamic: description-content-type
|
|
31
32
|
Dynamic: license
|
|
32
33
|
Dynamic: project-url
|
|
34
|
+
Dynamic: provides-extra
|
|
33
35
|
Dynamic: requires-dist
|
|
34
36
|
Dynamic: requires-python
|
|
35
37
|
Dynamic: summary
|
|
@@ -53,6 +53,7 @@ tests/autofix_files/async911_insert_library.py
|
|
|
53
53
|
tests/autofix_files/async913.py
|
|
54
54
|
tests/autofix_files/async913_trio_anyio.py
|
|
55
55
|
tests/autofix_files/async91x_autofix.py
|
|
56
|
+
tests/autofix_files/async91x_py311.py
|
|
56
57
|
tests/autofix_files/exception_suppress_context_manager.py
|
|
57
58
|
tests/autofix_files/exception_suppress_context_manager_import_star.py
|
|
58
59
|
tests/autofix_files/noqa.py
|
|
@@ -65,12 +66,14 @@ tests/eval_files/async100_simple_autofix.py
|
|
|
65
66
|
tests/eval_files/async101.py
|
|
66
67
|
tests/eval_files/async101_asyncio.py
|
|
67
68
|
tests/eval_files/async102.py
|
|
69
|
+
tests/eval_files/async102_120_py311.py
|
|
68
70
|
tests/eval_files/async102_aclose.py
|
|
69
71
|
tests/eval_files/async102_aclose_args.py
|
|
70
72
|
tests/eval_files/async102_anyio.py
|
|
71
73
|
tests/eval_files/async102_asyncio.py
|
|
72
74
|
tests/eval_files/async102_trio.py
|
|
73
75
|
tests/eval_files/async103.py
|
|
76
|
+
tests/eval_files/async103_104_py311.py
|
|
74
77
|
tests/eval_files/async103_all_imported.py
|
|
75
78
|
tests/eval_files/async103_both_imported.py
|
|
76
79
|
tests/eval_files/async103_no_104.py
|
|
@@ -127,10 +130,12 @@ tests/eval_files/async911.py
|
|
|
127
130
|
tests/eval_files/async911_insert_library.py
|
|
128
131
|
tests/eval_files/async912.py
|
|
129
132
|
tests/eval_files/async912_asyncio.py
|
|
133
|
+
tests/eval_files/async912_py311.py
|
|
130
134
|
tests/eval_files/async913.py
|
|
131
135
|
tests/eval_files/async913_trio_anyio.py
|
|
132
136
|
tests/eval_files/async91x_autofix.py
|
|
133
137
|
tests/eval_files/async91x_noautofix.py
|
|
138
|
+
tests/eval_files/async91x_py311.py
|
|
134
139
|
tests/eval_files/exception_suppress_context_manager.py
|
|
135
140
|
tests/eval_files/exception_suppress_context_manager_import_star.py
|
|
136
141
|
tests/eval_files/no_library.py
|
|
@@ -14,6 +14,34 @@ force-exclude = "tests/autofix_files/.*.py"
|
|
|
14
14
|
[tool.codespell]
|
|
15
15
|
ignore-words-list = 'spawnve'
|
|
16
16
|
|
|
17
|
+
[tool.coverage.coverage_conditional_plugin.rules]
|
|
18
|
+
no-cov-has-flake8 = "is_installed('flake8')"
|
|
19
|
+
no-cov-no-flake8 = "not is_installed('flake8')"
|
|
20
|
+
no-cov-py-lt-311 = "sys.version_info < (3, 11)"
|
|
21
|
+
|
|
22
|
+
[tool.coverage.paths]
|
|
23
|
+
source = [
|
|
24
|
+
"flake8_async",
|
|
25
|
+
"*/site-packages/flake8_async"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[tool.coverage.report]
|
|
29
|
+
exclude_lines = [
|
|
30
|
+
# Have to re-enable the standard pragma
|
|
31
|
+
"pragma: no cover",
|
|
32
|
+
# Don't complain about abstract methods, they aren't run:
|
|
33
|
+
"@(abc\\.)?abstractmethod",
|
|
34
|
+
# Don't check guarded type imports
|
|
35
|
+
"if (typing.)?TYPE_CHECKING:"
|
|
36
|
+
]
|
|
37
|
+
fail_under = 100
|
|
38
|
+
skip_covered = true
|
|
39
|
+
skip_empty = true
|
|
40
|
+
|
|
41
|
+
[tool.coverage.run]
|
|
42
|
+
branch = true
|
|
43
|
+
plugins = ["coverage_conditional_plugin"]
|
|
44
|
+
|
|
17
45
|
[tool.isort]
|
|
18
46
|
only_modified = true
|
|
19
47
|
profile = "black"
|
|
@@ -45,6 +73,10 @@ reportUnnecessaryTypeIgnoreComment = false
|
|
|
45
73
|
reportUnusedCallResult = false
|
|
46
74
|
strict = ["*.py", "tests/*.py", "flake8_async/**/*.py"]
|
|
47
75
|
|
|
76
|
+
[tool.pytest.ini_options]
|
|
77
|
+
filterwarnings = ["error"]
|
|
78
|
+
testpaths = ["tests"]
|
|
79
|
+
|
|
48
80
|
[tool.ruff]
|
|
49
81
|
extend-exclude = [
|
|
50
82
|
".*",
|
|
@@ -34,7 +34,8 @@ setup(
|
|
|
34
34
|
license_files=[], # https://github.com/pypa/twine/issues/1216
|
|
35
35
|
description="A highly opinionated flake8 plugin for Trio-related problems.",
|
|
36
36
|
zip_safe=False,
|
|
37
|
-
install_requires=["
|
|
37
|
+
install_requires=["libcst>=1.0.1"],
|
|
38
|
+
extras_require={"flake8": ["flake8>=6"]},
|
|
38
39
|
python_requires=">=3.9",
|
|
39
40
|
classifiers=[
|
|
40
41
|
"Development Status :: 3 - Alpha",
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Test for ASYNC91x rules with except* blocks.
|
|
2
|
+
|
|
3
|
+
ASYNC910: async-function-no-checkpoint
|
|
4
|
+
ASYNC911: async-generator-no-checkpoint
|
|
5
|
+
ASYNC913: indefinite-loop-no-guaranteed-checkpoint
|
|
6
|
+
|
|
7
|
+
async912 handled in separate file
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# ARG --enable=ASYNC910,ASYNC911,ASYNC913
|
|
11
|
+
# AUTOFIX
|
|
12
|
+
# ASYNCIO_NO_AUTOFIX
|
|
13
|
+
import trio
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def foo(): ...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def foo_try_except_star_1(): # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
20
|
+
try:
|
|
21
|
+
await foo()
|
|
22
|
+
except* ValueError:
|
|
23
|
+
...
|
|
24
|
+
except* RuntimeError:
|
|
25
|
+
raise
|
|
26
|
+
else:
|
|
27
|
+
await foo()
|
|
28
|
+
await trio.lowlevel.checkpoint()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def foo_try_except_star_2(): # safe
|
|
32
|
+
try:
|
|
33
|
+
...
|
|
34
|
+
except* ValueError:
|
|
35
|
+
...
|
|
36
|
+
finally:
|
|
37
|
+
await foo()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def foo_try_except_star_3(): # safe
|
|
41
|
+
try:
|
|
42
|
+
await foo()
|
|
43
|
+
except* ValueError:
|
|
44
|
+
raise
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Multiple except* handlers - should all guarantee checkpoint/raise
|
|
48
|
+
async def foo_try_except_star_4():
|
|
49
|
+
try:
|
|
50
|
+
await foo()
|
|
51
|
+
except* ValueError:
|
|
52
|
+
await foo()
|
|
53
|
+
except* TypeError:
|
|
54
|
+
raise
|
|
55
|
+
except* Exception:
|
|
56
|
+
raise
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def try_else_no_raise_in_except(): # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
60
|
+
try:
|
|
61
|
+
...
|
|
62
|
+
except* ValueError:
|
|
63
|
+
...
|
|
64
|
+
else:
|
|
65
|
+
await foo()
|
|
66
|
+
await trio.lowlevel.checkpoint()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def try_else_raise_in_except():
|
|
70
|
+
try:
|
|
71
|
+
...
|
|
72
|
+
except* ValueError:
|
|
73
|
+
raise
|
|
74
|
+
else:
|
|
75
|
+
await foo()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def check_async911(): # ASYNC911: 0, "exit", Statement("yield", lineno+7)
|
|
79
|
+
try:
|
|
80
|
+
await foo()
|
|
81
|
+
except* ValueError:
|
|
82
|
+
...
|
|
83
|
+
except* RuntimeError:
|
|
84
|
+
raise
|
|
85
|
+
await trio.lowlevel.checkpoint()
|
|
86
|
+
yield # ASYNC911: 4, "yield", Statement("function definition", lineno-7)
|
|
87
|
+
await trio.lowlevel.checkpoint()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def check_async913():
|
|
91
|
+
while True: # ASYNC913: 4
|
|
92
|
+
await trio.lowlevel.checkpoint()
|
|
93
|
+
try:
|
|
94
|
+
await foo()
|
|
95
|
+
except* ValueError:
|
|
96
|
+
# Missing checkpoint
|
|
97
|
+
...
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Test for ASYNC102/ASYNC120 with except*
|
|
2
|
+
|
|
3
|
+
ASYNC102: await-in-finally-or-cancelled
|
|
4
|
+
|
|
5
|
+
ASYNC120: await-in-except
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# type: ignore
|
|
9
|
+
# ARG --enable=ASYNC102,ASYNC120
|
|
10
|
+
# NOASYNCIO # TODO: support asyncio shields
|
|
11
|
+
import trio
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def foo():
|
|
15
|
+
try:
|
|
16
|
+
...
|
|
17
|
+
except* ValueError:
|
|
18
|
+
await foo() # ASYNC120: 8, Statement("except", lineno-1)
|
|
19
|
+
raise
|
|
20
|
+
except* BaseException:
|
|
21
|
+
await foo() # ASYNC102: 8, Statement("BaseException", lineno-1)
|
|
22
|
+
finally:
|
|
23
|
+
await foo() # ASYNC102: 8, Statement("try/finally", lineno-8)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
...
|
|
27
|
+
except* BaseException:
|
|
28
|
+
with trio.move_on_after(30, shield=True):
|
|
29
|
+
await foo()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Test for ASYNC103/ASYNC104 with except* blocks.
|
|
2
|
+
|
|
3
|
+
ASYNC103: no-reraise-cancelled
|
|
4
|
+
ASYNC104: cancelled-not-raised
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# ARG --enable=ASYNC103,ASYNC104
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
...
|
|
11
|
+
except* BaseException: # ASYNC103_trio: 8, "BaseException"
|
|
12
|
+
...
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
...
|
|
16
|
+
except* BaseException:
|
|
17
|
+
raise
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
...
|
|
21
|
+
except* ValueError:
|
|
22
|
+
...
|
|
23
|
+
except* BaseException: # ASYNC103_trio: 8, "BaseException"
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
...
|
|
28
|
+
except* BaseException:
|
|
29
|
+
raise ValueError # ASYNC104: 4
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def foo():
|
|
33
|
+
try:
|
|
34
|
+
...
|
|
35
|
+
except* BaseException: # ASYNC103_trio: 12, "BaseException"
|
|
36
|
+
return # ASYNC104: 8
|
|
37
|
+
try:
|
|
38
|
+
...
|
|
39
|
+
except* BaseException:
|
|
40
|
+
raise ValueError # ASYNC104: 8
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# ASYNC912 can't be tested with the other 91x rules since there's no universal
|
|
2
|
+
# cancelscope name across trio/asyncio/anyio - so we need ASYNCIO_NO_ERROR
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# ASYNCIO_NO_ERROR
|
|
6
|
+
async def foo(): ...
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def check_async912():
|
|
10
|
+
with trio.move_on_after(30): # ASYNC912: 9
|
|
11
|
+
try:
|
|
12
|
+
await foo()
|
|
13
|
+
except* ValueError:
|
|
14
|
+
# Missing checkpoint
|
|
15
|
+
...
|
|
16
|
+
await foo()
|