flake8-async 24.3.6__tar.gz → 24.4.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.3.6 → flake8_async-24.4.2}/CHANGELOG.md +6 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/PKG-INFO +12 -2
- {flake8-async-24.3.6 → flake8_async-24.4.2}/README.md +5 -1
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/__init__.py +1 -1
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor91x.py +3 -1
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitors.py +39 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async.egg-info/PKG-INFO +12 -2
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async.egg-info/SOURCES.txt +1 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/autofix_files/async910.py +13 -0
- flake8_async-24.4.2/tests/eval_files/async119.py +64 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async910.py +12 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_config_and_args.py +31 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_flake8_async.py +1 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tox.ini +12 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/CONTRIBUTING.md +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/LICENSE +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/MANIFEST.in +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/__main__.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/base.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/runner.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/__init__.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/flake8asyncvisitor.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/helpers.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor100.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor101.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor102.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor103_104.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor105.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor111.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor118.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor2xx.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async/visitors/visitor_utility.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async.egg-info/dependency_links.txt +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async.egg-info/entry_points.txt +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async.egg-info/not-zip-safe +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async.egg-info/requires.txt +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/flake8_async.egg-info/top_level.txt +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/pyproject.toml +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/setup.cfg +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/setup.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/__init__.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/autofix_files/async100.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/autofix_files/async100_simple_autofix.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/autofix_files/async911.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/autofix_files/async91x_autofix.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/autofix_files/noqa.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/autofix_files/noqa_testing.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/conftest.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/anyio_trio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async100.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async100_asyncio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async100_noautofix.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async100_simple_autofix.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async101.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async102.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async102_aclose.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async102_aclose_args.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async102_anyio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async102_asyncio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async102_trio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async103.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async103_all_imported.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async103_both_imported.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async103_no_104.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async103_trio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async104.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async104_anyio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async104_trio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async105.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async105_anyio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async106.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async109.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async110.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async111.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async112.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async113.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async113_anyio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async113_trio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async114.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async115.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async116.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async118.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async200.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async210.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async211.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async212.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async22x.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async22x_asyncio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async232.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async232_asyncio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async23x.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async23x_asyncio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async240.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async250.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async250_multi_library.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async251.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async251_multi_library.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async900.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async911.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async91x_autofix.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/async91x_noautofix.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/no_library.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/noqa.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/noqa_no_autofix.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/noqa_testing.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/eval_files/trio_anyio.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_all_visitors_imported.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_changelog_and_version.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_decorator.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_exception_on_invalid_code.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_formatting.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/test_messages_documented.py +0 -0
- {flake8-async-24.3.6 → flake8_async-24.4.2}/tests/trio_options.py +0 -0
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
*[CalVer, YY.month.patch](https://calver.org/)*
|
|
3
3
|
|
|
4
|
+
## 24.4.2
|
|
5
|
+
- Add ASYNC119: yield in contextmanager in async generator.
|
|
6
|
+
|
|
7
|
+
## 24.4.1
|
|
8
|
+
- ASYNC91X fix internal error caused by multiple `try/except` incorrectly sharing state.
|
|
9
|
+
|
|
4
10
|
## 24.3.6
|
|
5
11
|
- ASYNC100 no longer triggers if a context manager contains a `yield`.
|
|
6
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version: 24.
|
|
3
|
+
Version: 24.4.2
|
|
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
|
|
@@ -66,6 +66,7 @@ pip install flake8-async
|
|
|
66
66
|
- **ASYNC115**: Replace `[trio/anyio].sleep(0)` with the more suggestive `[trio/anyio].lowlevel.checkpoint()`.
|
|
67
67
|
- **ASYNC116**: `[trio/anyio].sleep()` with >24 hour interval should usually be `[trio/anyio].sleep_forever()`.
|
|
68
68
|
- **ASYNC118**: Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs.
|
|
69
|
+
- **ASYNC119**: `yield` in context manager in async generator is unsafe, the cleanup may be delayed until `await` is no longer allowed. We strongly encourage you to read PEP-533 and use `async with aclosing(...)`, or better yet avoid async generators entirely (see ASYNC900) in favor of context managers which return an iterable channel/queue.
|
|
69
70
|
|
|
70
71
|
### Warnings for blocking sync calls in async functions
|
|
71
72
|
Note: 22X, 23X and 24X has not had asyncio-specific suggestions written.
|
|
@@ -109,7 +110,7 @@ adding the following to your `.pre-commit-config.yaml`:
|
|
|
109
110
|
minimum_pre_commit_version: '2.9.0'
|
|
110
111
|
repos:
|
|
111
112
|
- repo: https://github.com/python-trio/flake8-async
|
|
112
|
-
rev: 24.
|
|
113
|
+
rev: 24.4.2
|
|
113
114
|
hooks:
|
|
114
115
|
- id: flake8-async
|
|
115
116
|
# args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
|
|
@@ -148,6 +149,9 @@ flake8-async **/*.py
|
|
|
148
149
|
but we prefer using a config file. The file needs to start with a section marker `[flake8]` and the following options are then parsed using flake8's config parser, and can be used just like any other flake8 options.
|
|
149
150
|
Note that it's not currently possible to use a configuration file when running `flake8-async` standalone.
|
|
150
151
|
|
|
152
|
+
### `ValueError` when trying to `ignore` error codes in config file
|
|
153
|
+
Error codes with more than three letters are not possible to `ignore` in config files since flake8>=6, as flake8 tries to validate correct configuration with a regex. We have decided not to conform to this, as it would be a breaking change for end-users requiring them to update `noqa`s and configurations, we think the `ASYNC` code is much more readable than e.g. `ASYxxx`, and ruff does not enforce such a limit. The easiest option for users hitting this error is to instead use the `--disable` option as documented [below](#--disable). See further discussion and other workarounds in https://github.com/python-trio/flake8-async/issues/230
|
|
154
|
+
|
|
151
155
|
### `--enable`
|
|
152
156
|
Comma-separated list of error codes to enable, similar to flake8 --select but is additionally more performant as it will disable non-enabled visitors from running instead of just silencing their errors.
|
|
153
157
|
|
|
@@ -221,6 +225,12 @@ async def my_function():
|
|
|
221
225
|
# Changelog
|
|
222
226
|
*[CalVer, YY.month.patch](https://calver.org/)*
|
|
223
227
|
|
|
228
|
+
## 24.4.2
|
|
229
|
+
- Add ASYNC119: yield in contextmanager in async generator.
|
|
230
|
+
|
|
231
|
+
## 24.4.1
|
|
232
|
+
- ASYNC91X fix internal error caused by multiple `try/except` incorrectly sharing state.
|
|
233
|
+
|
|
224
234
|
## 24.3.6
|
|
225
235
|
- ASYNC100 no longer triggers if a context manager contains a `yield`.
|
|
226
236
|
|
|
@@ -41,6 +41,7 @@ pip install flake8-async
|
|
|
41
41
|
- **ASYNC115**: Replace `[trio/anyio].sleep(0)` with the more suggestive `[trio/anyio].lowlevel.checkpoint()`.
|
|
42
42
|
- **ASYNC116**: `[trio/anyio].sleep()` with >24 hour interval should usually be `[trio/anyio].sleep_forever()`.
|
|
43
43
|
- **ASYNC118**: Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs.
|
|
44
|
+
- **ASYNC119**: `yield` in context manager in async generator is unsafe, the cleanup may be delayed until `await` is no longer allowed. We strongly encourage you to read PEP-533 and use `async with aclosing(...)`, or better yet avoid async generators entirely (see ASYNC900) in favor of context managers which return an iterable channel/queue.
|
|
44
45
|
|
|
45
46
|
### Warnings for blocking sync calls in async functions
|
|
46
47
|
Note: 22X, 23X and 24X has not had asyncio-specific suggestions written.
|
|
@@ -84,7 +85,7 @@ adding the following to your `.pre-commit-config.yaml`:
|
|
|
84
85
|
minimum_pre_commit_version: '2.9.0'
|
|
85
86
|
repos:
|
|
86
87
|
- repo: https://github.com/python-trio/flake8-async
|
|
87
|
-
rev: 24.
|
|
88
|
+
rev: 24.4.2
|
|
88
89
|
hooks:
|
|
89
90
|
- id: flake8-async
|
|
90
91
|
# args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
|
|
@@ -123,6 +124,9 @@ flake8-async **/*.py
|
|
|
123
124
|
but we prefer using a config file. The file needs to start with a section marker `[flake8]` and the following options are then parsed using flake8's config parser, and can be used just like any other flake8 options.
|
|
124
125
|
Note that it's not currently possible to use a configuration file when running `flake8-async` standalone.
|
|
125
126
|
|
|
127
|
+
### `ValueError` when trying to `ignore` error codes in config file
|
|
128
|
+
Error codes with more than three letters are not possible to `ignore` in config files since flake8>=6, as flake8 tries to validate correct configuration with a regex. We have decided not to conform to this, as it would be a breaking change for end-users requiring them to update `noqa`s and configurations, we think the `ASYNC` code is much more readable than e.g. `ASYxxx`, and ruff does not enforce such a limit. The easiest option for users hitting this error is to instead use the `--disable` option as documented [below](#--disable). See further discussion and other workarounds in https://github.com/python-trio/flake8-async/issues/230
|
|
129
|
+
|
|
126
130
|
### `--enable`
|
|
127
131
|
Comma-separated list of error codes to enable, similar to flake8 --select but is additionally more performant as it will disable non-enabled visitors from running instead of just silencing their errors.
|
|
128
132
|
|
|
@@ -454,7 +454,7 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
454
454
|
def visit_Try(self, node: cst.Try):
|
|
455
455
|
if not self.async_function:
|
|
456
456
|
return
|
|
457
|
-
self.save_state(node, "try_state")
|
|
457
|
+
self.save_state(node, "try_state", copy=True)
|
|
458
458
|
# except & finally guaranteed to enter with checkpoint if checkpointed
|
|
459
459
|
# before try and no yield in try body.
|
|
460
460
|
self.try_state.body_uncheckpointed_statements = (
|
|
@@ -596,6 +596,8 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
596
596
|
self.save_state(
|
|
597
597
|
node,
|
|
598
598
|
"uncheckpointed_statements",
|
|
599
|
+
# reference is overwritten below so don't need to copy
|
|
600
|
+
copy=False,
|
|
599
601
|
)
|
|
600
602
|
|
|
601
603
|
# inject an artificial uncheckpointed statement that won't raise an error,
|
|
@@ -281,6 +281,45 @@ class Visitor116(Flake8AsyncVisitor):
|
|
|
281
281
|
self.error(node, m[2])
|
|
282
282
|
|
|
283
283
|
|
|
284
|
+
@error_class
|
|
285
|
+
class Visitor119(Flake8AsyncVisitor):
|
|
286
|
+
error_codes: Mapping[str, str] = {
|
|
287
|
+
"ASYNC119": "Yield in contextmanager in async generator might not trigger"
|
|
288
|
+
" cleanup. Use `@asynccontextmanager` or refactor."
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
|
292
|
+
super().__init__(*args, **kwargs)
|
|
293
|
+
self.unsafe_function: bool = False
|
|
294
|
+
self.contextmanager: bool = False
|
|
295
|
+
|
|
296
|
+
def visit_AsyncFunctionDef(
|
|
297
|
+
self, node: ast.AsyncFunctionDef | ast.FunctionDef | ast.Lambda
|
|
298
|
+
):
|
|
299
|
+
self.save_state(node, "unsafe_function", "contextmanager")
|
|
300
|
+
self.contextmanager = False
|
|
301
|
+
if isinstance(node, ast.AsyncFunctionDef) and not has_decorator(
|
|
302
|
+
node, "asynccontextmanager"
|
|
303
|
+
):
|
|
304
|
+
self.unsafe_function = True
|
|
305
|
+
else:
|
|
306
|
+
self.unsafe_function = False
|
|
307
|
+
|
|
308
|
+
def visit_With(self, node: ast.With | ast.AsyncWith):
|
|
309
|
+
self.save_state(node, "contextmanager")
|
|
310
|
+
self.contextmanager = True
|
|
311
|
+
|
|
312
|
+
def visit_Yield(self, node: ast.Yield):
|
|
313
|
+
if self.unsafe_function and self.contextmanager:
|
|
314
|
+
self.error(node)
|
|
315
|
+
|
|
316
|
+
visit_AsyncWith = visit_With
|
|
317
|
+
visit_FunctionDef = visit_AsyncFunctionDef
|
|
318
|
+
# it's not possible to yield or open context managers in lambda's, so this
|
|
319
|
+
# one isn't strictly needed afaik.
|
|
320
|
+
visit_Lambda = visit_AsyncFunctionDef
|
|
321
|
+
|
|
322
|
+
|
|
284
323
|
@error_class
|
|
285
324
|
@disabled_by_default
|
|
286
325
|
class Visitor900(Flake8AsyncVisitor):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flake8-async
|
|
3
|
-
Version: 24.
|
|
3
|
+
Version: 24.4.2
|
|
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
|
|
@@ -66,6 +66,7 @@ pip install flake8-async
|
|
|
66
66
|
- **ASYNC115**: Replace `[trio/anyio].sleep(0)` with the more suggestive `[trio/anyio].lowlevel.checkpoint()`.
|
|
67
67
|
- **ASYNC116**: `[trio/anyio].sleep()` with >24 hour interval should usually be `[trio/anyio].sleep_forever()`.
|
|
68
68
|
- **ASYNC118**: Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs.
|
|
69
|
+
- **ASYNC119**: `yield` in context manager in async generator is unsafe, the cleanup may be delayed until `await` is no longer allowed. We strongly encourage you to read PEP-533 and use `async with aclosing(...)`, or better yet avoid async generators entirely (see ASYNC900) in favor of context managers which return an iterable channel/queue.
|
|
69
70
|
|
|
70
71
|
### Warnings for blocking sync calls in async functions
|
|
71
72
|
Note: 22X, 23X and 24X has not had asyncio-specific suggestions written.
|
|
@@ -109,7 +110,7 @@ adding the following to your `.pre-commit-config.yaml`:
|
|
|
109
110
|
minimum_pre_commit_version: '2.9.0'
|
|
110
111
|
repos:
|
|
111
112
|
- repo: https://github.com/python-trio/flake8-async
|
|
112
|
-
rev: 24.
|
|
113
|
+
rev: 24.4.2
|
|
113
114
|
hooks:
|
|
114
115
|
- id: flake8-async
|
|
115
116
|
# args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
|
|
@@ -148,6 +149,9 @@ flake8-async **/*.py
|
|
|
148
149
|
but we prefer using a config file. The file needs to start with a section marker `[flake8]` and the following options are then parsed using flake8's config parser, and can be used just like any other flake8 options.
|
|
149
150
|
Note that it's not currently possible to use a configuration file when running `flake8-async` standalone.
|
|
150
151
|
|
|
152
|
+
### `ValueError` when trying to `ignore` error codes in config file
|
|
153
|
+
Error codes with more than three letters are not possible to `ignore` in config files since flake8>=6, as flake8 tries to validate correct configuration with a regex. We have decided not to conform to this, as it would be a breaking change for end-users requiring them to update `noqa`s and configurations, we think the `ASYNC` code is much more readable than e.g. `ASYxxx`, and ruff does not enforce such a limit. The easiest option for users hitting this error is to instead use the `--disable` option as documented [below](#--disable). See further discussion and other workarounds in https://github.com/python-trio/flake8-async/issues/230
|
|
154
|
+
|
|
151
155
|
### `--enable`
|
|
152
156
|
Comma-separated list of error codes to enable, similar to flake8 --select but is additionally more performant as it will disable non-enabled visitors from running instead of just silencing their errors.
|
|
153
157
|
|
|
@@ -221,6 +225,12 @@ async def my_function():
|
|
|
221
225
|
# Changelog
|
|
222
226
|
*[CalVer, YY.month.patch](https://calver.org/)*
|
|
223
227
|
|
|
228
|
+
## 24.4.2
|
|
229
|
+
- Add ASYNC119: yield in contextmanager in async generator.
|
|
230
|
+
|
|
231
|
+
## 24.4.1
|
|
232
|
+
- ASYNC91X fix internal error caused by multiple `try/except` incorrectly sharing state.
|
|
233
|
+
|
|
224
234
|
## 24.3.6
|
|
225
235
|
- ASYNC100 no longer triggers if a context manager contains a `yield`.
|
|
226
236
|
|
|
@@ -601,3 +601,16 @@ async def await_in_gen_target():
|
|
|
601
601
|
async def await_everywhere_except_gen_target(): # error: 0, "exit", Statement("function definition", lineno)
|
|
602
602
|
(await x async for x in bar())
|
|
603
603
|
await trio.lowlevel.checkpoint()
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
# Issue #226
|
|
607
|
+
async def fn_226(): # error: 0, "exit", Statement("function definition", lineno)
|
|
608
|
+
try:
|
|
609
|
+
while foo():
|
|
610
|
+
try:
|
|
611
|
+
await trio.sleep(1)
|
|
612
|
+
except Exception:
|
|
613
|
+
pass
|
|
614
|
+
except Exception:
|
|
615
|
+
pass
|
|
616
|
+
await trio.lowlevel.checkpoint()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def unsafe_yield():
|
|
7
|
+
with open(""):
|
|
8
|
+
yield # error: 8
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def async_with():
|
|
12
|
+
async with unsafe_yield():
|
|
13
|
+
yield # error: 8
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def warn_on_yeach_yield():
|
|
17
|
+
with open(""):
|
|
18
|
+
yield # error: 8
|
|
19
|
+
yield # error: 8
|
|
20
|
+
with open(""):
|
|
21
|
+
yield # error: 8
|
|
22
|
+
yield # error: 8
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def yield_not_in_contextmanager():
|
|
26
|
+
yield
|
|
27
|
+
with open(""):
|
|
28
|
+
...
|
|
29
|
+
yield
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def yield_in_nested_function():
|
|
33
|
+
with open(""):
|
|
34
|
+
|
|
35
|
+
def foo():
|
|
36
|
+
yield
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def yield_in_nested_async_function():
|
|
40
|
+
with open(""):
|
|
41
|
+
|
|
42
|
+
async def foo():
|
|
43
|
+
yield
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def yield_after_nested_async_function():
|
|
47
|
+
with open(""):
|
|
48
|
+
|
|
49
|
+
async def foo():
|
|
50
|
+
yield
|
|
51
|
+
|
|
52
|
+
yield # error: 8
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@asynccontextmanager
|
|
56
|
+
async def safe_in_contextmanager():
|
|
57
|
+
with open(""):
|
|
58
|
+
yield
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@contextlib.asynccontextmanager
|
|
62
|
+
async def safe_in_contextmanager2():
|
|
63
|
+
with open(""):
|
|
64
|
+
yield
|
|
@@ -572,3 +572,15 @@ async def await_in_gen_target():
|
|
|
572
572
|
|
|
573
573
|
async def await_everywhere_except_gen_target(): # error: 0, "exit", Statement("function definition", lineno)
|
|
574
574
|
(await x async for x in bar())
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
# Issue #226
|
|
578
|
+
async def fn_226(): # error: 0, "exit", Statement("function definition", lineno)
|
|
579
|
+
try:
|
|
580
|
+
while foo():
|
|
581
|
+
try:
|
|
582
|
+
await trio.sleep(1)
|
|
583
|
+
except Exception:
|
|
584
|
+
pass
|
|
585
|
+
except Exception:
|
|
586
|
+
pass
|
|
@@ -210,6 +210,7 @@ trio200-blocking-calls =
|
|
|
210
210
|
other -> async,
|
|
211
211
|
sync_fns.* -> the_async_equivalent,
|
|
212
212
|
select = ASYNC200
|
|
213
|
+
extend-ignore = E
|
|
213
214
|
"""
|
|
214
215
|
)
|
|
215
216
|
assert tmp_path.joinpath("example.py").write_text(
|
|
@@ -403,3 +404,33 @@ def test_disable_noqa_ast(
|
|
|
403
404
|
== "./example.py:1:1: ASYNC106 trio must be imported with `import trio` for the"
|
|
404
405
|
" linter to work.\n"
|
|
405
406
|
)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@pytest.mark.xfail(reason="flake8>=6 enforces three-letter error codes in config")
|
|
410
|
+
def test_config_ignore_error_code(tmp_path: Path) -> None:
|
|
411
|
+
assert tmp_path.joinpath(".flake8").write_text(
|
|
412
|
+
"""
|
|
413
|
+
[flake8]
|
|
414
|
+
ignore = ASYNC100
|
|
415
|
+
"""
|
|
416
|
+
)
|
|
417
|
+
res = subprocess.run(
|
|
418
|
+
["flake8", "--help"], cwd=tmp_path, capture_output=True, check=False
|
|
419
|
+
)
|
|
420
|
+
assert not res.stderr
|
|
421
|
+
assert res.returncode == 0
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# but make sure we can disable selected codes
|
|
425
|
+
def test_config_disable_error_code(tmp_path: Path) -> None:
|
|
426
|
+
# select ASYNC200 and create file that induces ASYNC200
|
|
427
|
+
_test_trio200_from_config_common(tmp_path)
|
|
428
|
+
# disable ASYNC200
|
|
429
|
+
with open(tmp_path.joinpath(".flake8"), "a", encoding="utf-8") as file:
|
|
430
|
+
file.write("disable = ASYNC200")
|
|
431
|
+
|
|
432
|
+
# it now returns no errors
|
|
433
|
+
res = subprocess.run(["flake8"], cwd=tmp_path, capture_output=True, check=True)
|
|
434
|
+
assert not res.stdout
|
|
435
|
+
assert not res.stderr
|
|
436
|
+
assert res.returncode == 0
|
|
@@ -416,6 +416,7 @@ def _parse_eval_file(
|
|
|
416
416
|
msg = f"Line {lineno}: Failed to format\n {message!r}\nwith\n{args}"
|
|
417
417
|
raise ParseError(msg) from e
|
|
418
418
|
|
|
419
|
+
assert enabled_codes, "no codes enabled. Fix file name or add `# ARGS --enable=...`"
|
|
419
420
|
enabled_codes_list = enabled_codes.split(",")
|
|
420
421
|
for code in enabled_codes_list:
|
|
421
422
|
assert re.fullmatch(
|
|
@@ -20,6 +20,18 @@ deps =
|
|
|
20
20
|
commands =
|
|
21
21
|
pytest {posargs:-n auto}
|
|
22
22
|
|
|
23
|
+
[testenv:docs]
|
|
24
|
+
description = Generate docs locally
|
|
25
|
+
deps =
|
|
26
|
+
sphinx
|
|
27
|
+
readthedocs-sphinx-ext
|
|
28
|
+
-r docs/requirements.txt
|
|
29
|
+
allowlist_externals = make
|
|
30
|
+
changedir = docs
|
|
31
|
+
skip_install = True
|
|
32
|
+
commands =
|
|
33
|
+
make html
|
|
34
|
+
|
|
23
35
|
# Settings for other tools
|
|
24
36
|
[pytest]
|
|
25
37
|
addopts =
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|