flake8-async 24.3.2__tar.gz → 24.3.4__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.
Files changed (110) hide show
  1. {flake8-async-24.3.2 → flake8-async-24.3.4}/CHANGELOG.md +6 -0
  2. {flake8-async-24.3.2 → flake8-async-24.3.4}/PKG-INFO +10 -3
  3. {flake8-async-24.3.2 → flake8-async-24.3.4}/README.md +3 -2
  4. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/__init__.py +1 -1
  5. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/__init__.py +1 -1
  6. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor2xx.py +15 -3
  7. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitors.py +13 -1
  8. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async.egg-info/PKG-INFO +10 -3
  9. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async.egg-info/SOURCES.txt +2 -0
  10. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async110.py +5 -0
  11. flake8-async-24.3.4/tests/eval_files/async251.py +13 -0
  12. flake8-async-24.3.4/tests/eval_files/async251_multi_library.py +9 -0
  13. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_flake8_async.py +11 -7
  14. {flake8-async-24.3.2 → flake8-async-24.3.4}/CONTRIBUTING.md +0 -0
  15. {flake8-async-24.3.2 → flake8-async-24.3.4}/LICENSE +0 -0
  16. {flake8-async-24.3.2 → flake8-async-24.3.4}/MANIFEST.in +0 -0
  17. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/__main__.py +0 -0
  18. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/base.py +0 -0
  19. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/runner.py +0 -0
  20. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/flake8asyncvisitor.py +0 -0
  21. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/helpers.py +0 -0
  22. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor100.py +0 -0
  23. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor101.py +0 -0
  24. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor102.py +0 -0
  25. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor103_104.py +0 -0
  26. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor105.py +0 -0
  27. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor111.py +0 -0
  28. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor118.py +0 -0
  29. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor91x.py +0 -0
  30. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async/visitors/visitor_utility.py +0 -0
  31. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async.egg-info/dependency_links.txt +0 -0
  32. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async.egg-info/entry_points.txt +0 -0
  33. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async.egg-info/not-zip-safe +0 -0
  34. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async.egg-info/requires.txt +0 -0
  35. {flake8-async-24.3.2 → flake8-async-24.3.4}/flake8_async.egg-info/top_level.txt +0 -0
  36. {flake8-async-24.3.2 → flake8-async-24.3.4}/pyproject.toml +0 -0
  37. {flake8-async-24.3.2 → flake8-async-24.3.4}/setup.cfg +0 -0
  38. {flake8-async-24.3.2 → flake8-async-24.3.4}/setup.py +0 -0
  39. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/__init__.py +0 -0
  40. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/autofix_files/async100.py +0 -0
  41. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/autofix_files/async100_simple_autofix.py +0 -0
  42. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/autofix_files/async910.py +0 -0
  43. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/autofix_files/async911.py +0 -0
  44. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/autofix_files/async91x_autofix.py +0 -0
  45. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/autofix_files/noqa.py +0 -0
  46. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/autofix_files/noqa_testing.py +0 -0
  47. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/conftest.py +0 -0
  48. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/anyio_trio.py +0 -0
  49. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async100.py +0 -0
  50. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async100_asyncio.py +0 -0
  51. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async100_noautofix.py +0 -0
  52. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async100_simple_autofix.py +0 -0
  53. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async101.py +0 -0
  54. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async102.py +0 -0
  55. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async102_anyio.py +0 -0
  56. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async102_asyncio.py +0 -0
  57. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async102_trio.py +0 -0
  58. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async103.py +0 -0
  59. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async103_all_imported.py +0 -0
  60. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async103_both_imported.py +0 -0
  61. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async103_no_104.py +0 -0
  62. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async103_trio.py +0 -0
  63. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async104.py +0 -0
  64. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async104_anyio.py +0 -0
  65. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async104_trio.py +0 -0
  66. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async105.py +0 -0
  67. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async105_anyio.py +0 -0
  68. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async106.py +0 -0
  69. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async109.py +0 -0
  70. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async111.py +0 -0
  71. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async112.py +0 -0
  72. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async113.py +0 -0
  73. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async113_anyio.py +0 -0
  74. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async113_trio.py +0 -0
  75. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async114.py +0 -0
  76. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async115.py +0 -0
  77. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async116.py +0 -0
  78. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async118.py +0 -0
  79. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async200.py +0 -0
  80. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async210.py +0 -0
  81. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async211.py +0 -0
  82. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async212.py +0 -0
  83. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async22x.py +0 -0
  84. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async22x_asyncio.py +0 -0
  85. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async232.py +0 -0
  86. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async232_asyncio.py +0 -0
  87. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async23x.py +0 -0
  88. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async23x_asyncio.py +0 -0
  89. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async240.py +0 -0
  90. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async250.py +0 -0
  91. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async250_multi_library.py +0 -0
  92. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async900.py +0 -0
  93. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async910.py +0 -0
  94. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async911.py +0 -0
  95. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async91x_autofix.py +0 -0
  96. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/async91x_noautofix.py +0 -0
  97. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/no_library.py +0 -0
  98. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/noqa.py +0 -0
  99. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/noqa_no_autofix.py +0 -0
  100. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/noqa_testing.py +0 -0
  101. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/eval_files/trio_anyio.py +0 -0
  102. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_all_visitors_imported.py +0 -0
  103. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_changelog_and_version.py +0 -0
  104. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_config_and_args.py +0 -0
  105. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_decorator.py +0 -0
  106. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_exception_on_invalid_code.py +0 -0
  107. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_formatting.py +0 -0
  108. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/test_messages_documented.py +0 -0
  109. {flake8-async-24.3.2 → flake8-async-24.3.4}/tests/trio_options.py +0 -0
  110. {flake8-async-24.3.2 → flake8-async-24.3.4}/tox.ini +0 -0
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
  *[CalVer, YY.month.patch](https://calver.org/)*
3
3
 
4
+ ## 24.3.4
5
+ - ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()`
6
+
7
+ ## 24.3.3
8
+ - Add ASYNC251: `time.sleep()` in async method.
9
+
4
10
  ## 24.3.2
5
11
  - Add ASYNC250: blocking sync call `input()` in async method.
6
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flake8-async
3
- Version: 24.3.2
3
+ Version: 24.3.4
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
@@ -80,7 +80,8 @@ Note: 22X, 23X and 24X has not had asyncio-specific suggestions written.
80
80
  - **ASYNC231**: Sync IO call in async function, use `[trio/anyio].wrap_file(...)`. `asyncio` users need to use a library such as [aiofiles](https://pypi.org/project/aiofiles/), or switch to [anyio](https://github.com/agronholm/anyio).
81
81
  - **ASYNC232**: Blocking sync call on file object, wrap the file object in `[trio/anyio].wrap_file()` to get an async file object.
82
82
  - **ASYNC240**: Avoid using `os.path` in async functions, prefer using `[trio/anyio].Path` objects. `asyncio` users should consider [aiopath](https://pypi.org/project/aiopath) or [anyio](https://github.com/agronholm/anyio).
83
- - **ASYNC250**: Builtin `input()` should not be called from async function.
83
+ - **ASYNC250**: Builtin `input()` should not be called from async function. Wrap in `[trio/anyio].to_thread.run_sync()` or `asyncio.loop.run_in_executor()`.
84
+ - **ASYNC251**: `time.sleep(...)` should not be called from async function. Use `[trio/anyio/asyncio].sleep(...)`.
84
85
 
85
86
  ### Warnings disabled by default
86
87
  - **ASYNC900**: Async generator without `@asynccontextmanager` not allowed. You might want to enable this on a codebase since async generators are inherently unsafe and cleanup logic might not be performed. See https://github.com/python-trio/flake8-async/issues/211 and https://discuss.python.org/t/using-exceptiongroup-at-anthropic-experience-report/20888/6 for discussion.
@@ -108,7 +109,7 @@ adding the following to your `.pre-commit-config.yaml`:
108
109
  minimum_pre_commit_version: '2.9.0'
109
110
  repos:
110
111
  - repo: https://github.com/python-trio/flake8-async
111
- rev: 24.3.2
112
+ rev: 24.3.4
112
113
  hooks:
113
114
  - id: flake8-async
114
115
  # args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
@@ -220,6 +221,12 @@ async def my_function():
220
221
  # Changelog
221
222
  *[CalVer, YY.month.patch](https://calver.org/)*
222
223
 
224
+ ## 24.3.4
225
+ - ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()`
226
+
227
+ ## 24.3.3
228
+ - Add ASYNC251: `time.sleep()` in async method.
229
+
223
230
  ## 24.3.2
224
231
  - Add ASYNC250: blocking sync call `input()` in async method.
225
232
 
@@ -55,7 +55,8 @@ Note: 22X, 23X and 24X has not had asyncio-specific suggestions written.
55
55
  - **ASYNC231**: Sync IO call in async function, use `[trio/anyio].wrap_file(...)`. `asyncio` users need to use a library such as [aiofiles](https://pypi.org/project/aiofiles/), or switch to [anyio](https://github.com/agronholm/anyio).
56
56
  - **ASYNC232**: Blocking sync call on file object, wrap the file object in `[trio/anyio].wrap_file()` to get an async file object.
57
57
  - **ASYNC240**: Avoid using `os.path` in async functions, prefer using `[trio/anyio].Path` objects. `asyncio` users should consider [aiopath](https://pypi.org/project/aiopath) or [anyio](https://github.com/agronholm/anyio).
58
- - **ASYNC250**: Builtin `input()` should not be called from async function.
58
+ - **ASYNC250**: Builtin `input()` should not be called from async function. Wrap in `[trio/anyio].to_thread.run_sync()` or `asyncio.loop.run_in_executor()`.
59
+ - **ASYNC251**: `time.sleep(...)` should not be called from async function. Use `[trio/anyio/asyncio].sleep(...)`.
59
60
 
60
61
  ### Warnings disabled by default
61
62
  - **ASYNC900**: Async generator without `@asynccontextmanager` not allowed. You might want to enable this on a codebase since async generators are inherently unsafe and cleanup logic might not be performed. See https://github.com/python-trio/flake8-async/issues/211 and https://discuss.python.org/t/using-exceptiongroup-at-anthropic-experience-report/20888/6 for discussion.
@@ -83,7 +84,7 @@ adding the following to your `.pre-commit-config.yaml`:
83
84
  minimum_pre_commit_version: '2.9.0'
84
85
  repos:
85
86
  - repo: https://github.com/python-trio/flake8-async
86
- rev: 24.3.2
87
+ rev: 24.3.4
87
88
  hooks:
88
89
  - id: flake8-async
89
90
  # args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
@@ -37,7 +37,7 @@ if TYPE_CHECKING:
37
37
 
38
38
 
39
39
  # CalVer: YY.month.patch, e.g. first release of July 2022 == "22.7.1"
40
- __version__ = "24.3.2"
40
+ __version__ = "24.3.4"
41
41
 
42
42
 
43
43
  # taken from https://github.com/Zac-HD/shed
@@ -25,7 +25,7 @@ default_disabled_error_codes: list[str] = []
25
25
  utility_visitors: set[type[Flake8AsyncVisitor]] = set()
26
26
  utility_visitors_cst: set[type[Flake8AsyncVisitor_cst]] = set()
27
27
 
28
- # Import all visitors so their decorators run, filling the above containers
28
+ # Import all files with visitors so their decorators run, filling the above containers
29
29
  # This has to be done at the end to avoid circular imports
30
30
  from . import (
31
31
  visitor2xx,
@@ -398,13 +398,25 @@ wrappers: Mapping[str, str] = {
398
398
  class Visitor25X(Visitor200):
399
399
  error_codes: Mapping[str, str] = {
400
400
  "ASYNC250": ("Blocking sync call `input()` in async function. Wrap in `{}`."),
401
+ "ASYNC251": (
402
+ "Blocking sync call `time.sleep(...)` in async function."
403
+ " Use `await {}.sleep(...)`."
404
+ ),
401
405
  }
402
406
 
403
407
  def visit_Call(self, node: ast.Call):
404
408
  if not self.async_function:
405
409
  return
406
- if isinstance(node.func, ast.Name) and node.func.id == "input":
410
+ func_name = ast.unparse(node.func)
411
+ if func_name == "input":
412
+ error_code = "ASYNC250"
407
413
  if len(self.library) == 1:
408
- self.error(node, wrappers[self.library_str])
414
+ msg_param = wrappers[self.library_str]
409
415
  else:
410
- self.error(node, "/".join(wrappers[lib] for lib in self.library))
416
+ msg_param = "/".join(wrappers[lib] for lib in self.library)
417
+ elif func_name == "time.sleep":
418
+ error_code = "ASYNC251"
419
+ msg_param = self.library_str
420
+ else:
421
+ return
422
+ self.error(node, msg_param, error_code=error_code)
@@ -65,7 +65,19 @@ class Visitor110(Flake8AsyncVisitor):
65
65
  len(node.body) == 1
66
66
  and isinstance(node.body[0], ast.Expr)
67
67
  and isinstance(node.body[0].value, ast.Await)
68
- and get_matching_call(node.body[0].value.value, "sleep", "sleep_until")
68
+ and (
69
+ get_matching_call(node.body[0].value.value, "sleep", "sleep_until")
70
+ or (
71
+ # get_matching_call doesn't (currently) support checking for trio.x.y
72
+ isinstance(call := node.body[0].value.value, ast.Call)
73
+ and isinstance(call.func, ast.Attribute)
74
+ and call.func.attr == "checkpoint"
75
+ and isinstance(call.func.value, ast.Attribute)
76
+ and call.func.value.attr == "lowlevel"
77
+ and isinstance(call.func.value.value, ast.Name)
78
+ and call.func.value.value.id in ("trio", "anyio")
79
+ )
80
+ )
69
81
  ):
70
82
  self.error(node, self.library_str)
71
83
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flake8-async
3
- Version: 24.3.2
3
+ Version: 24.3.4
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
@@ -80,7 +80,8 @@ Note: 22X, 23X and 24X has not had asyncio-specific suggestions written.
80
80
  - **ASYNC231**: Sync IO call in async function, use `[trio/anyio].wrap_file(...)`. `asyncio` users need to use a library such as [aiofiles](https://pypi.org/project/aiofiles/), or switch to [anyio](https://github.com/agronholm/anyio).
81
81
  - **ASYNC232**: Blocking sync call on file object, wrap the file object in `[trio/anyio].wrap_file()` to get an async file object.
82
82
  - **ASYNC240**: Avoid using `os.path` in async functions, prefer using `[trio/anyio].Path` objects. `asyncio` users should consider [aiopath](https://pypi.org/project/aiopath) or [anyio](https://github.com/agronholm/anyio).
83
- - **ASYNC250**: Builtin `input()` should not be called from async function.
83
+ - **ASYNC250**: Builtin `input()` should not be called from async function. Wrap in `[trio/anyio].to_thread.run_sync()` or `asyncio.loop.run_in_executor()`.
84
+ - **ASYNC251**: `time.sleep(...)` should not be called from async function. Use `[trio/anyio/asyncio].sleep(...)`.
84
85
 
85
86
  ### Warnings disabled by default
86
87
  - **ASYNC900**: Async generator without `@asynccontextmanager` not allowed. You might want to enable this on a codebase since async generators are inherently unsafe and cleanup logic might not be performed. See https://github.com/python-trio/flake8-async/issues/211 and https://discuss.python.org/t/using-exceptiongroup-at-anthropic-experience-report/20888/6 for discussion.
@@ -108,7 +109,7 @@ adding the following to your `.pre-commit-config.yaml`:
108
109
  minimum_pre_commit_version: '2.9.0'
109
110
  repos:
110
111
  - repo: https://github.com/python-trio/flake8-async
111
- rev: 24.3.2
112
+ rev: 24.3.4
112
113
  hooks:
113
114
  - id: flake8-async
114
115
  # args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
@@ -220,6 +221,12 @@ async def my_function():
220
221
  # Changelog
221
222
  *[CalVer, YY.month.patch](https://calver.org/)*
222
223
 
224
+ ## 24.3.4
225
+ - ASYNC110 (don't loop sleep) now also warns if looping `[trio/anyio].lowlevel.checkpoint()`
226
+
227
+ ## 24.3.3
228
+ - Add ASYNC251: `time.sleep()` in async method.
229
+
223
230
  ## 24.3.2
224
231
  - Add ASYNC250: blocking sync call `input()` in async method.
225
232
 
@@ -94,6 +94,8 @@ tests/eval_files/async23x_asyncio.py
94
94
  tests/eval_files/async240.py
95
95
  tests/eval_files/async250.py
96
96
  tests/eval_files/async250_multi_library.py
97
+ tests/eval_files/async251.py
98
+ tests/eval_files/async251_multi_library.py
97
99
  tests/eval_files/async900.py
98
100
  tests/eval_files/async910.py
99
101
  tests/eval_files/async911.py
@@ -69,3 +69,8 @@ async def foo():
69
69
 
70
70
  while await trio.sleep():
71
71
  ...
72
+
73
+ # also error when looping .lowlevel.checkpoint, which is equivalent to .sleep(0)
74
+ # see https://github.com/python-trio/flake8-async/issues/201
75
+ while ...: # ASYNC110: 4, "trio"
76
+ await trio.lowlevel.checkpoint()
@@ -0,0 +1,13 @@
1
+ # NOAUTOFIX
2
+
3
+ import time
4
+ from time import sleep
5
+
6
+
7
+ async def foo():
8
+ time.sleep(5) # ASYNC251: 4, "trio"
9
+ time.sleep(5) if 5 else time.sleep(5) # ASYNC251: 4, "trio" # ASYNC251: 28, "trio"
10
+
11
+ # Not handled due to difficulty tracking imports and not wanting to trigger
12
+ # false positives. But could definitely be handled by ruff et al.
13
+ sleep(5)
@@ -0,0 +1,9 @@
1
+ # BASE_LIBRARY trio
2
+ # NOASYNCIO # tests asyncio without replacing for it
3
+ import trio
4
+ import time
5
+ import asyncio
6
+
7
+
8
+ async def foo():
9
+ time.sleep(5) # ASYNC251: 4, "[trio/asyncio]"
@@ -458,20 +458,24 @@ class SyncTransformer(ast.NodeTransformer):
458
458
  def visit_Await(self, node: ast.Await):
459
459
  return self.generic_visit(node.value)
460
460
 
461
- def replace_async(self, node: ast.AST, target: type[ast.AST]) -> ast.AST:
461
+ def replace_async(
462
+ self, node: ast.AST, target: type[ast.AST], *args: object
463
+ ) -> ast.AST:
462
464
  node = self.generic_visit(node)
463
- newnode = target()
465
+ # py313 gives DeprecationWarning if missing posargs, so we pass them even if we
466
+ # overwrite __dict__ directly afterwards
467
+ newnode = target(*args)
464
468
  newnode.__dict__ = node.__dict__
465
469
  return newnode
466
470
 
467
- def visit_AsyncFunctionDef(self, node: ast.AST):
468
- return self.replace_async(node, ast.FunctionDef)
471
+ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef):
472
+ return self.replace_async(node, ast.FunctionDef, node.name, node.args)
469
473
 
470
- def visit_AsyncWith(self, node: ast.AST):
474
+ def visit_AsyncWith(self, node: ast.AsyncWith):
471
475
  return self.replace_async(node, ast.With)
472
476
 
473
- def visit_AsyncFor(self, node: ast.AST):
474
- return self.replace_async(node, ast.For)
477
+ def visit_AsyncFor(self, node: ast.AsyncFor):
478
+ return self.replace_async(node, ast.For, node.target, node.iter)
475
479
 
476
480
 
477
481
  @pytest.mark.parametrize(("test", "path"), test_files)
File without changes
File without changes
File without changes
File without changes
File without changes