diff --git a/docs/changelog.rst b/docs/changelog.rst
index 87b7b726..f306eea6 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,11 @@ Changelog
`CalVer, YY.month.patch `_
+24.9.2
+======
+- Fix false alarm in :ref:`ASYNC113 ` and :ref:`ASYNC121 ` with sync functions nested inside an async function.
+
+
24.9.1
======
- Add :ref:`ASYNC121 ` control-flow-in-taskgroup
diff --git a/flake8_async/__init__.py b/flake8_async/__init__.py
index d4827f86..6ac873fc 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.9.1"
+__version__ = "24.9.2"
# taken from https://github.com/Zac-HD/shed
diff --git a/flake8_async/visitors/visitors.py b/flake8_async/visitors/visitors.py
index 6d9ca23f..ec7ee1d5 100644
--- a/flake8_async/visitors/visitors.py
+++ b/flake8_async/visitors/visitors.py
@@ -188,6 +188,11 @@ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef):
node, "asynccontextmanager"
)
+ def visit_FunctionDef(self, node: ast.FunctionDef):
+ self.save_state(node, "aenter")
+ # sync function should never be named __aenter__ or have @asynccontextmanager
+ self.aenter = False
+
def visit_Yield(self, node: ast.Yield):
self.aenter = False
@@ -398,10 +403,12 @@ def visit_Return(self, node: ast.Return) -> None:
if unsafe_cm in self.unsafe_stack:
self.error(node, "return", unsafe_cm)
- def visit_FunctionDef(self, node: ast.FunctionDef):
+ def visit_FunctionDef(self, node: ast.FunctionDef | ast.AsyncFunctionDef):
self.save_state(node, "unsafe_stack", copy=True)
self.unsafe_stack = []
+ visit_AsyncFunctionDef = visit_FunctionDef
+
@error_class_cst
class Visitor300(Flake8AsyncVisitor_cst):
diff --git a/tests/eval_files/async113.py b/tests/eval_files/async113.py
index ebddc5be..22fedc28 100644
--- a/tests/eval_files/async113.py
+++ b/tests/eval_files/async113.py
@@ -105,3 +105,13 @@ async def __aenter__(self):
async def __aexit__(self, *args):
assert self.moo is not None
await self.nursery_manager.__aexit__(*args)
+
+
+@asynccontextmanager
+async def foo_nested_sync_def():
+ with trio.open_nursery() as bar:
+
+ def non_async_func():
+ bar.start_soon(trio.run_process)
+
+ yield
diff --git a/tests/eval_files/async121.py b/tests/eval_files/async121.py
index 78a6b6c4..b55f21b9 100644
--- a/tests/eval_files/async121.py
+++ b/tests/eval_files/async121.py
@@ -30,6 +30,9 @@ async def foo_return_nested():
def bar():
return # safe
+ async def bar():
+ return # safe
+
async def foo_while_safe():
async with trio.open_nursery():