flake8-async 25.2.3__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.
Files changed (147) hide show
  1. {flake8_async-25.2.3 → flake8_async-25.3.1}/PKG-INFO +1 -1
  2. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/__init__.py +5 -4
  3. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/helpers.py +1 -1
  4. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor102_120.py +3 -1
  5. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor103_104.py +3 -1
  6. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor123.py +1 -2
  7. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor91x.py +25 -11
  8. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async.egg-info/PKG-INFO +1 -1
  9. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async.egg-info/SOURCES.txt +5 -0
  10. {flake8_async-25.2.3 → flake8_async-25.3.1}/pyproject.toml +32 -0
  11. flake8_async-25.3.1/tests/autofix_files/async91x_py311.py +97 -0
  12. flake8_async-25.3.1/tests/eval_files/async102_120_py311.py +29 -0
  13. flake8_async-25.3.1/tests/eval_files/async103_104_py311.py +40 -0
  14. flake8_async-25.3.1/tests/eval_files/async912_py311.py +16 -0
  15. flake8_async-25.3.1/tests/eval_files/async91x_py311.py +92 -0
  16. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/test_config_and_args.py +19 -1
  17. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/test_flake8_async.py +5 -0
  18. {flake8_async-25.2.3 → flake8_async-25.3.1}/tox.ini +4 -28
  19. {flake8_async-25.2.3 → flake8_async-25.3.1}/CHANGELOG.md +0 -0
  20. {flake8_async-25.2.3 → flake8_async-25.3.1}/CONTRIBUTING.md +0 -0
  21. {flake8_async-25.2.3 → flake8_async-25.3.1}/LICENSE +0 -0
  22. {flake8_async-25.2.3 → flake8_async-25.3.1}/MANIFEST.in +0 -0
  23. {flake8_async-25.2.3 → flake8_async-25.3.1}/README.md +0 -0
  24. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/__main__.py +0 -0
  25. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/base.py +0 -0
  26. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/runner.py +0 -0
  27. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/__init__.py +0 -0
  28. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/flake8asyncvisitor.py +0 -0
  29. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor101.py +0 -0
  30. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor105.py +0 -0
  31. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor111.py +0 -0
  32. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor118.py +0 -0
  33. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor2xx.py +0 -0
  34. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitor_utility.py +0 -0
  35. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async/visitors/visitors.py +0 -0
  36. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async.egg-info/dependency_links.txt +0 -0
  37. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async.egg-info/entry_points.txt +0 -0
  38. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async.egg-info/not-zip-safe +0 -0
  39. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async.egg-info/requires.txt +0 -0
  40. {flake8_async-25.2.3 → flake8_async-25.3.1}/flake8_async.egg-info/top_level.txt +0 -0
  41. {flake8_async-25.2.3 → flake8_async-25.3.1}/setup.cfg +0 -0
  42. {flake8_async-25.2.3 → flake8_async-25.3.1}/setup.py +0 -0
  43. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/__init__.py +0 -0
  44. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async100.py +0 -0
  45. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async100_asyncio.py +0 -0
  46. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async100_simple_autofix.py +0 -0
  47. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async124.py +0 -0
  48. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async910.py +0 -0
  49. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async910_insert_library.py +0 -0
  50. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async911.py +0 -0
  51. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async911_insert_library.py +0 -0
  52. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async913.py +0 -0
  53. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async913_trio_anyio.py +0 -0
  54. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/async91x_autofix.py +0 -0
  55. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/exception_suppress_context_manager.py +0 -0
  56. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/exception_suppress_context_manager_import_star.py +0 -0
  57. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/noqa.py +0 -0
  58. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/autofix_files/noqa_testing.py +0 -0
  59. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/check_changelog_and_version.py +0 -0
  60. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/conftest.py +0 -0
  61. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/anyio_trio.py +0 -0
  62. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async100.py +0 -0
  63. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async100_asyncio.py +0 -0
  64. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async100_noautofix.py +0 -0
  65. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async100_simple_autofix.py +0 -0
  66. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async101.py +0 -0
  67. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async101_asyncio.py +0 -0
  68. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async102.py +0 -0
  69. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async102_aclose.py +0 -0
  70. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async102_aclose_args.py +0 -0
  71. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async102_anyio.py +0 -0
  72. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async102_asyncio.py +0 -0
  73. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async102_trio.py +0 -0
  74. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async103.py +0 -0
  75. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async103_all_imported.py +0 -0
  76. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async103_both_imported.py +0 -0
  77. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async103_no_104.py +0 -0
  78. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async103_trio.py +0 -0
  79. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async104.py +0 -0
  80. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async104_anyio.py +0 -0
  81. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async104_trio.py +0 -0
  82. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async105.py +0 -0
  83. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async105_anyio.py +0 -0
  84. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async106.py +0 -0
  85. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async109.py +0 -0
  86. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async110.py +0 -0
  87. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async111.py +0 -0
  88. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async111_asyncio.py +0 -0
  89. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async112.py +0 -0
  90. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async112_asyncio.py +0 -0
  91. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async113.py +0 -0
  92. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async113_asyncio.py +0 -0
  93. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async113_trio.py +0 -0
  94. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async114.py +0 -0
  95. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async115.py +0 -0
  96. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async116.py +0 -0
  97. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async118.py +0 -0
  98. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async119.py +0 -0
  99. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async120.py +0 -0
  100. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async121.py +0 -0
  101. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async121_anyio.py +0 -0
  102. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async121_asyncio.py +0 -0
  103. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async122.py +0 -0
  104. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async123.py +0 -0
  105. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async123_py311.py +0 -0
  106. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async124.py +0 -0
  107. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async124_no_autofix.py +0 -0
  108. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async200.py +0 -0
  109. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async210.py +0 -0
  110. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async211.py +0 -0
  111. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async212.py +0 -0
  112. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async22x.py +0 -0
  113. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async22x_asyncio.py +0 -0
  114. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async232.py +0 -0
  115. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async232_asyncio.py +0 -0
  116. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async23x.py +0 -0
  117. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async23x_asyncio.py +0 -0
  118. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async240.py +0 -0
  119. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async250.py +0 -0
  120. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async250_multi_library.py +0 -0
  121. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async251.py +0 -0
  122. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async251_multi_library.py +0 -0
  123. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async300.py +0 -0
  124. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async900.py +0 -0
  125. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async910.py +0 -0
  126. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async910_insert_library.py +0 -0
  127. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async911.py +0 -0
  128. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async911_insert_library.py +0 -0
  129. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async912.py +0 -0
  130. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async912_asyncio.py +0 -0
  131. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async913.py +0 -0
  132. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async913_trio_anyio.py +0 -0
  133. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async91x_autofix.py +0 -0
  134. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/async91x_noautofix.py +0 -0
  135. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/exception_suppress_context_manager.py +0 -0
  136. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/exception_suppress_context_manager_import_star.py +0 -0
  137. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/no_library.py +0 -0
  138. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/noqa.py +0 -0
  139. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/noqa_no_autofix.py +0 -0
  140. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/noqa_testing.py +0 -0
  141. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/eval_files/trio_anyio.py +0 -0
  142. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/test_all_visitors_imported.py +0 -0
  143. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/test_decorator.py +0 -0
  144. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/test_exception_on_invalid_code.py +0 -0
  145. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/test_formatting.py +0 -0
  146. {flake8_async-25.2.3 → flake8_async-25.3.1}/tests/test_messages_documented.py +0 -0
  147. {flake8_async-25.2.3 → 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.2.3
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
@@ -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.2.3"
41
+ __version__ = "25.3.1"
42
42
 
43
43
 
44
44
  # taken from https://github.com/Zac-HD/shed
@@ -138,7 +138,7 @@ class Plugin:
138
138
  self.module: cst.Module = cst_parse_module_native(source)
139
139
 
140
140
  @classmethod
141
- def from_filename(cls, filename: str | PathLike[str]) -> Plugin: # pragma: no cover
141
+ def from_filename(cls, filename: str | PathLike[str]) -> Plugin:
142
142
  # only used with --runslow
143
143
  with tokenize.open(filename) as f:
144
144
  source = f.read()
@@ -204,7 +204,7 @@ class Plugin:
204
204
  'lines with "# noqa" at the end.'
205
205
  ),
206
206
  )
207
- else: # if run as a flake8 plugin
207
+ else: # pragma: no-cov-no-flake8
208
208
  Plugin.standalone = False
209
209
  # Disable ASYNC9xx calls by default
210
210
  option_manager.extend_default_ignore(default_disabled_error_codes)
@@ -380,7 +380,7 @@ class Plugin:
380
380
  }
381
381
  assert all_codes
382
382
 
383
- if options.autofix and not Plugin.standalone:
383
+ if options.autofix and not Plugin.standalone: # pragma: no-cov-no-flake8
384
384
  print("Cannot autofix when run as a flake8 plugin.", file=sys.stderr)
385
385
  sys.exit(1)
386
386
  autofix_codes = set(get_matching_codes(options.autofix, all_codes))
@@ -457,6 +457,7 @@ def parse_async200_dict(raw_value: str) -> dict[str, str]:
457
457
 
458
458
 
459
459
  # not run if flake8 is installed
460
+ # TODO: this is not tested at all atm, I'm not even sure if it works
460
461
  def parse_per_file_disable( # pragma: no cover
461
462
  raw_value: str,
462
463
  ) -> dict[str, tuple[str, ...]]:
@@ -371,7 +371,7 @@ def with_has_call(
371
371
 
372
372
  """
373
373
  if isinstance(base, str):
374
- base = (base,) # pragma: no cover
374
+ base = (base,)
375
375
 
376
376
  # build matcher, using SaveMatchedNode to save the base and the function name.
377
377
  matcher = m.Call(
@@ -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
- # we run strict codecov on all python versions, this one doesn't run on <py311
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: # pragma: no branch
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, original_node: cst.ExceptHandler, updated_node: cst.ExceptHandler
802
- ) -> cst.ExceptHandler:
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(self, original_node: cst.Try, updated_node: cst.Try) -> cst.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.2.3
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
@@ -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
  ".*",
@@ -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()
@@ -0,0 +1,92 @@
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
+
29
+
30
+ async def foo_try_except_star_2(): # safe
31
+ try:
32
+ ...
33
+ except* ValueError:
34
+ ...
35
+ finally:
36
+ await foo()
37
+
38
+
39
+ async def foo_try_except_star_3(): # safe
40
+ try:
41
+ await foo()
42
+ except* ValueError:
43
+ raise
44
+
45
+
46
+ # Multiple except* handlers - should all guarantee checkpoint/raise
47
+ async def foo_try_except_star_4():
48
+ try:
49
+ await foo()
50
+ except* ValueError:
51
+ await foo()
52
+ except* TypeError:
53
+ raise
54
+ except* Exception:
55
+ raise
56
+
57
+
58
+ async def try_else_no_raise_in_except(): # ASYNC910: 0, "exit", Statement("function definition", lineno)
59
+ try:
60
+ ...
61
+ except* ValueError:
62
+ ...
63
+ else:
64
+ await foo()
65
+
66
+
67
+ async def try_else_raise_in_except():
68
+ try:
69
+ ...
70
+ except* ValueError:
71
+ raise
72
+ else:
73
+ await foo()
74
+
75
+
76
+ async def check_async911(): # ASYNC911: 0, "exit", Statement("yield", lineno+7)
77
+ try:
78
+ await foo()
79
+ except* ValueError:
80
+ ...
81
+ except* RuntimeError:
82
+ raise
83
+ yield # ASYNC911: 4, "yield", Statement("function definition", lineno-7)
84
+
85
+
86
+ async def check_async913():
87
+ while True: # ASYNC913: 4
88
+ try:
89
+ await foo()
90
+ except* ValueError:
91
+ # Missing checkpoint
92
+ ...
@@ -282,6 +282,24 @@ def test_async200_from_config_subprocess(tmp_path: Path):
282
282
  assert res.stdout == err_msg.encode("ascii")
283
283
 
284
284
 
285
+ def test_trio200_warning(tmp_path: Path):
286
+ fpath = tmp_path / "foo.py"
287
+ fpath.touch()
288
+ res = subprocess.run(
289
+ ["flake8-async", "--trio200-blocking-calls=foo->bar", "foo.py"],
290
+ cwd=tmp_path,
291
+ capture_output=True,
292
+ check=False,
293
+ encoding="utf8",
294
+ )
295
+ assert res.returncode == 0
296
+ assert res.stderr.endswith(
297
+ "UserWarning: trio200-blocking-calls has been deprecated in favor of "
298
+ "async200-blocking-calls\n warnings.warn(\n"
299
+ )
300
+ assert not res.stdout
301
+
302
+
285
303
  @pytest.mark.skipif(flake8 is None, reason="flake8 is not installed")
286
304
  def test_async200_from_config_subprocess_cli_ignore(tmp_path: Path):
287
305
  _ = _test_async200_from_config_common(tmp_path)
@@ -297,7 +315,7 @@ def test_async200_from_config_subprocess_cli_ignore(tmp_path: Path):
297
315
  assert res.returncode == 0
298
316
 
299
317
 
300
- def test_900_default_off(capsys: pytest.CaptureFixture[str]):
318
+ def test_900_default_off():
301
319
  res = subprocess.run(
302
320
  ["flake8-async", "tests/eval_files/async900.py"],
303
321
  capture_output=True,
@@ -422,6 +422,10 @@ def _parse_eval_file(
422
422
  if not line or line[0] == "#":
423
423
  continue
424
424
 
425
+ # skip lines that *don't* have a comment
426
+ if "#" not in line:
427
+ continue
428
+
425
429
  # get text between `error:` and (end of line or another comment)
426
430
  k = re.findall(r"(error|ASYNC...)(_.*)?:([^#]*)(?=#|$)", line)
427
431
 
@@ -539,6 +543,7 @@ class SyncTransformer(ast.NodeTransformer):
539
543
  def test_noerror_on_sync_code(test: str, path: Path):
540
544
  if any(e in test for e in error_codes_ignored_when_checking_transformed_sync_code):
541
545
  return
546
+ check_version(test)
542
547
  with tokenize.open(path) as f:
543
548
  source = f.read()
544
549
  tree = SyncTransformer().visit(ast.parse(source))
@@ -11,14 +11,16 @@ deps =
11
11
  flake8_7: flake8>=7.0
12
12
  flake8_6: flake8>=6.0, <7.0
13
13
  pytest
14
- pytest-cov
14
+ pytest-cov # to make it easy to pass --no-cov
15
+ coverage
16
+ coverage-conditional-plugin
15
17
  pytest-xdist
16
18
  hypothesis
17
19
  # 0.3.3 adds py313 support
18
20
  hypothesmith >= 0.3.3
19
21
  trio
20
22
  commands =
21
- pytest {posargs:-n auto}
23
+ coverage run -m pytest {posargs:-n auto}
22
24
 
23
25
  [testenv:docs]
24
26
  description = Generate docs locally
@@ -32,29 +34,3 @@ skip_install = True
32
34
  commands =
33
35
  make clean
34
36
  make html
35
-
36
- # Settings for other tools
37
- [pytest]
38
- addopts =
39
- --cov=flake8_async
40
- --cov-branch
41
- --cov-report=term-missing:skip-covered
42
- --cov-fail-under=100
43
- filterwarnings =
44
- error
45
-
46
- [coverage:paths]
47
- source =
48
- flake8_async
49
- */site-packages/flake8_async
50
-
51
- [coverage:report]
52
- exclude_lines =
53
- # Have to re-enable the standard pragma
54
- pragma: no cover
55
-
56
- # Don't complain about abstract methods, they aren't run:
57
- @(abc\.)?abstractmethod
58
-
59
- # Don't check guarded type imports
60
- if (typing.)?TYPE_CHECKING:
File without changes
File without changes
File without changes
File without changes
File without changes