diff --git a/docs/changelog.rst b/docs/changelog.rst index d81bb8af..2672b087 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,10 @@ Changelog `CalVer, YY.month.patch `_ +24.10.2 +======= +- :ref:`ASYNC102 ` now also warns about ``await()`` inside ``__aexit__``. + 24.10.1 ======= - Add :ref:`ASYNC123 ` bad-exception-group-flattening diff --git a/docs/rules.rst b/docs/rules.rst index c3c8edbc..c3b7566d 100644 --- a/docs/rules.rst +++ b/docs/rules.rst @@ -21,8 +21,8 @@ ASYNC101 : yield-in-cancel-scope This has substantial overlap with :ref:`ASYNC119 `, which will warn on almost all instances of ASYNC101, but ASYNC101 is about a conceptually different problem that will not get resolved by `PEP 533 `_. _`ASYNC102` : await-in-finally-or-cancelled - ``await`` inside ``finally`` or :ref:`cancelled-catching ` ``except:`` must have shielded :ref:`cancel scope ` with timeout. - If not, the async call will immediately raise a new cancellation, suppressing the cancellation that was caught. + ``await`` inside ``finally``, :ref:`cancelled-catching ` ``except:``, or ``__aexit__`` must have shielded :ref:`cancel scope ` with timeout. + If not, the async call will immediately raise a new cancellation, suppressing any cancellation that was caught. See :ref:`ASYNC120 ` for the general case where other exceptions might get suppressed. This is currently not able to detect asyncio shields. diff --git a/flake8_async/__init__.py b/flake8_async/__init__.py index b9bbaf99..83c5d570 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.10.1" +__version__ = "24.10.2" # taken from https://github.com/Zac-HD/shed diff --git a/flake8_async/visitors/__init__.py b/flake8_async/visitors/__init__.py index f1b61995..652e8850 100644 --- a/flake8_async/visitors/__init__.py +++ b/flake8_async/visitors/__init__.py @@ -31,7 +31,7 @@ visitor2xx, visitor91x, visitor101, - visitor102, + visitor102_120, visitor103_104, visitor105, visitor111, diff --git a/flake8_async/visitors/visitor102.py b/flake8_async/visitors/visitor102_120.py similarity index 96% rename from flake8_async/visitors/visitor102.py rename to flake8_async/visitors/visitor102_120.py index e1837cb9..61f1e32f 100644 --- a/flake8_async/visitors/visitor102.py +++ b/flake8_async/visitors/visitor102_120.py @@ -1,4 +1,8 @@ -"""Visitor102, which warns on unprotected `await` inside `finally`. +"""Contains Visitor102 with ASYNC102 and ASYNC120. + +ASYNC102: await-in-finally-or-cancelled +ASYNC120: await-in-except + To properly protect they must be inside a shielded cancel scope with a timeout. """ @@ -222,6 +226,10 @@ def visit_FunctionDef( self._potential_120 = [] + # lambda doesn't have `name` attribute + if getattr(node, "name", None) == "__aexit__": + self._critical_scope = Statement("__aexit__", node.lineno, node.col_offset) + visit_AsyncFunctionDef = visit_FunctionDef # lambda can't contain await, try, except, raise, with, or assignments. # You also can't do assignment expressions with attributes. So we don't need to diff --git a/pyproject.toml b/pyproject.toml index 8d49c151..b48579e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,6 +65,7 @@ ignore = [ "D105", "D106", "D107", + "D400", # ends-in-period, stricter version of ends-in-punctuation "S101", "D203", # one-blank-line-before-class "D213", # multi-line-summary-second-line diff --git a/tests/eval_files/async102.py b/tests/eval_files/async102.py index 732fd7a7..a52333b6 100644 --- a/tests/eval_files/async102.py +++ b/tests/eval_files/async102.py @@ -304,3 +304,8 @@ async def foo_nested_cs(): await foo() # error: 12, Statement("bare except", lineno-12) cs1.shield = True await foo() + + +# treat __aexit__ as a critical scope +async def __aexit__(): + await foo() # error: 4, Statement("__aexit__", lineno-1)