diff --git a/docs/changelog.rst b/docs/changelog.rst
index dcfb1a16..c420d15a 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,10 @@ Changelog
`CalVer, YY.month.patch `_
+24.11.3
+=======
+- Revert :ref:`ASYNC100 ` ignoring :func:`trio.open_nursery` and :func:`anyio.create_task_group` due to it not viewing `start_soon()` as introducing a :ref:`cancel point `.
+
24.11.2
=======
- Fix crash in ``Visitor91x`` on ``async with a().b():``.
@@ -11,8 +15,8 @@ Changelog
24.11.1
=======
- :ref:`ASYNC100 ` now ignores :func:`trio.open_nursery` and :func:`anyio.create_task_group`
- as cancellation sources, because they are :ref:`schedule points ` but not
- :ref:`cancellation points `.
+ as cancellation sources, because they are :ref:`schedule points ` but not
+ :ref:`cancellation points `.
- :ref:`ASYNC101 ` and :ref:`ASYNC119 ` are now silenced for decorators in :ref:`transform-async-generator-decorators`.
24.10.2
@@ -34,7 +38,6 @@ Changelog
24.9.3
======
- :ref:`ASYNC102 ` and :ref:`ASYNC120 `:
-
- handles nested cancel scopes
- detects internal cancel scopes of nurseries as a way to shield&deadline
- no longer treats :func:`trio.open_nursery` or :func:`anyio.create_task_group` as cancellation sources
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 36574f32..2868b95b 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -99,7 +99,7 @@ functions defined by Trio will either checkpoint or raise an exception when
iteration, and when exhausting the iterator, and ``async with`` will checkpoint
on at least one of enter/exit.
-The one exception is :func:`trio.open_nursery` and :func:`anyio.create_task_group` which are :ref:`schedule_points` but not :ref:`cancel_points`.
+The one exception is :func:`trio.open_nursery` and :func:`anyio.create_task_group` which are :ref:`schedule points ` but not :ref:`cancel points `.
asyncio does not place any guarantees on if or when asyncio functions will
checkpoint. This means that enabling and adhering to :ref:`ASYNC91x `
@@ -117,7 +117,6 @@ To insert a checkpoint with no other side effects, you can use
`
.. _schedule_point:
-.. _schedule_points:
Schedule Point
--------------
@@ -137,7 +136,6 @@ asyncio does not have any direct equivalents due to their cancellation model bei
.. _cancel_point:
-.. _cancel_points:
Cancel Point
------------
diff --git a/docs/rules.rst b/docs/rules.rst
index 3d835fc2..41668c1d 100644
--- a/docs/rules.rst
+++ b/docs/rules.rst
@@ -13,7 +13,6 @@ _`ASYNC100` : cancel-scope-no-checkpoint
A :ref:`timeout_context` does not contain any :ref:`checkpoints `.
This makes it pointless, as the timeout can only be triggered by a checkpoint.
This check also treats ``yield`` as a checkpoint, since checkpoints can happen in the caller we yield to.
- :func:`trio.open_nursery` and :func:`anyio.create_task_group` are excluded, as they are :ref:`schedule_points` but not :ref:`cancel_points`.
See :ref:`ASYNC912 ` which will in addition guarantee checkpoints on every code path.
_`ASYNC101` : yield-in-cancel-scope
diff --git a/flake8_async/__init__.py b/flake8_async/__init__.py
index 8ffe4b05..2710c5e2 100644
--- a/flake8_async/__init__.py
+++ b/flake8_async/__init__.py
@@ -38,7 +38,7 @@
# CalVer: YY.month.patch, e.g. first release of July 2022 == "22.7.1"
-__version__ = "24.11.2"
+__version__ = "24.11.3"
# taken from https://github.com/Zac-HD/shed
diff --git a/flake8_async/visitors/visitor91x.py b/flake8_async/visitors/visitor91x.py
index 202e0a99..539fcee6 100644
--- a/flake8_async/visitors/visitor91x.py
+++ b/flake8_async/visitors/visitor91x.py
@@ -25,7 +25,6 @@
flatten_preserving_comments,
fnmatch_qualified_name_cst,
func_has_decorator,
- identifier_to_string,
iter_guaranteed_once_cst,
with_has_call,
)
@@ -492,33 +491,12 @@ def _is_exception_suppressing_context_manager(self, node: cst.With) -> bool:
is not None
)
- def _checkpoint_with(self, node: cst.With):
- """Conditionally checkpoints entry/exit of With.
-
- If the with only contains calls to open_nursery/create_task_group, it's a schedule
- point but not a cancellation point, so we treat it as a checkpoint for async91x
- but not for async100.
- """
- if getattr(node, "asynchronous", None):
- for item in node.items:
- if not (
- isinstance(item.item, cst.Call)
- and identifier_to_string(item.item.func)
- in (
- "trio.open_nursery",
- "anyio.create_task_group",
- )
- ):
- self.checkpoint()
- break
- else:
- self.uncheckpointed_statements = set()
-
# Async context managers can reasonably checkpoint on either or both of entry and
# exit. Given that we can't tell which, we assume "both" to avoid raising a
# missing-checkpoint warning when there might in fact be one (i.e. a false alarm).
def visit_With_body(self, node: cst.With):
- self._checkpoint_with(node)
+ if getattr(node, "asynchronous", None):
+ self.checkpoint()
# if this might suppress exceptions, we cannot treat anything inside it as
# checkpointing.
@@ -577,7 +555,8 @@ def leave_With(self, original_node: cst.With, updated_node: cst.With):
self.restore_state(original_node)
self.uncheckpointed_statements.update(prev_checkpoints)
- self._checkpoint_with(original_node)
+ if getattr(original_node, "asynchronous", None):
+ self.checkpoint()
return updated_node
# error if no checkpoint since earlier yield or function entry
diff --git a/tests/autofix_files/async100.py b/tests/autofix_files/async100.py
index ccaf16b5..609f0349 100644
--- a/tests/autofix_files/async100.py
+++ b/tests/autofix_files/async100.py
@@ -133,9 +133,9 @@ async def fn(timeout):
async def nursery_no_cancel_point():
- # error: 9, "trio", "CancelScope"
- async with anyio.create_task_group():
- ...
+ with trio.CancelScope(): # should error, but reverted PR
+ async with anyio.create_task_group():
+ ...
async def dont_crash_on_non_name_or_attr_call():
diff --git a/tests/autofix_files/async100.py.diff b/tests/autofix_files/async100.py.diff
index 7f074e60..268f7d82 100644
--- a/tests/autofix_files/async100.py.diff
+++ b/tests/autofix_files/async100.py.diff
@@ -130,16 +130,3 @@
with contextlib.suppress(Exception):
with open("blah") as file:
-@@ x,9 x,9 @@
-
-
- async def nursery_no_cancel_point():
-- with trio.CancelScope(): # error: 9, "trio", "CancelScope"
-- async with anyio.create_task_group():
-- ...
-+ # error: 9, "trio", "CancelScope"
-+ async with anyio.create_task_group():
-+ ...
-
-
- async def dont_crash_on_non_name_or_attr_call():
diff --git a/tests/autofix_files/async100_trio.py b/tests/autofix_files/async100_trio.py
index 8ab2dd5b..25958142 100644
--- a/tests/autofix_files/async100_trio.py
+++ b/tests/autofix_files/async100_trio.py
@@ -5,6 +5,6 @@
async def nursery_no_cancel_point():
- # error: 9, "trio", "CancelScope"
- async with trio.open_nursery():
- ...
+ with trio.CancelScope(): # should error, but reverted PR
+ async with trio.open_nursery():
+ ...
diff --git a/tests/autofix_files/async100_trio.py.diff b/tests/autofix_files/async100_trio.py.diff
index dd355aae..e69de29b 100644
--- a/tests/autofix_files/async100_trio.py.diff
+++ b/tests/autofix_files/async100_trio.py.diff
@@ -1,12 +0,0 @@
----
-+++
-@@ x,6 x,6 @@
-
-
- async def nursery_no_cancel_point():
-- with trio.CancelScope(): # error: 9, "trio", "CancelScope"
-- async with trio.open_nursery():
-- ...
-+ # error: 9, "trio", "CancelScope"
-+ async with trio.open_nursery():
-+ ...
diff --git a/tests/eval_files/async100.py b/tests/eval_files/async100.py
index f862eea6..b7857a5c 100644
--- a/tests/eval_files/async100.py
+++ b/tests/eval_files/async100.py
@@ -133,7 +133,7 @@ async def fn(timeout):
async def nursery_no_cancel_point():
- with trio.CancelScope(): # error: 9, "trio", "CancelScope"
+ with trio.CancelScope(): # should error, but reverted PR
async with anyio.create_task_group():
...
diff --git a/tests/eval_files/async100_trio.py b/tests/eval_files/async100_trio.py
index ccf5a1ea..25958142 100644
--- a/tests/eval_files/async100_trio.py
+++ b/tests/eval_files/async100_trio.py
@@ -5,6 +5,6 @@
async def nursery_no_cancel_point():
- with trio.CancelScope(): # error: 9, "trio", "CancelScope"
+ with trio.CancelScope(): # should error, but reverted PR
async with trio.open_nursery():
...
diff --git a/tests/test_flake8_async.py b/tests/test_flake8_async.py
index 9676bbcc..c88af167 100644
--- a/tests/test_flake8_async.py
+++ b/tests/test_flake8_async.py
@@ -83,6 +83,8 @@ def format_difflib_line(s: str) -> str:
def diff_strings(first: str, second: str, /) -> str:
+ if first == second:
+ return ""
return (
"".join(
map(