Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@ Changelog

`CalVer, YY.month.patch <https://calver.org/>`_

24.11.2
=======
- Fix crash in ``Visitor91x`` on ``async with a().b():``.

24.11.1
=======
- :ref:`ASYNC100 <async100>` now ignores :func:`trio.open_nursery` and :func:`anyio.create_task_group`
as cancellation sources, because they are :ref:`schedule points <schedule_points>` but not
:ref:`cancellation points <cancel_points>`.
- :ref:`ASYNC101 <async101>` and :ref:`ASYNC119 <async119>` are now silenced for decorators in :ref:`transform-async-generator-decorators`.

24.10.2
=======
- :ref:`ASYNC101 <async101>` and :ref:`ASYNC119 <async119>` are now silenced for decorators in :ref:`transform-async-generator-decorators`
- :ref:`ASYNC102 <async102>` now also warns about ``await()`` inside ``__aexit__``.

24.10.1
=======
- Add :ref:`ASYNC123 <async123>` bad-exception-group-flattening
- Add :ref:`ASYNC123 <async123>` bad-exception-group-flattening.

24.9.5
======
Expand Down
2 changes: 1 addition & 1 deletion flake8_async/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@


# CalVer: YY.month.patch, e.g. first release of July 2022 == "22.7.1"
__version__ = "24.11.1"
__version__ = "24.11.2"


# taken from https://github.com/Zac-HD/shed
Expand Down
23 changes: 14 additions & 9 deletions flake8_async/visitors/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,15 +337,20 @@ def build_cst_matcher(attr: str) -> m.BaseExpression:
return m.Attribute(value=build_cst_matcher(body), attr=m.Name(value=tail))


def identifier_to_string(attr: cst.Name | cst.Attribute) -> str | None:
if isinstance(attr, cst.Name):
return attr.value
if not isinstance(attr.value, (cst.Attribute, cst.Name)):
return None
lhs = identifier_to_string(attr.value)
if lhs is None:
return None
return lhs + "." + attr.attr.value
def identifier_to_string(node: cst.CSTNode) -> str | None:
"""Convert a simple identifier to a string.

If the node is composed of anything but cst.Name + cst.Attribute it returns None.
"""
if isinstance(node, cst.Name):
return node.value
if (
isinstance(node, cst.Attribute)
and (lhs := identifier_to_string(node.value)) is not None
):
return lhs + "." + node.attr.value

return None


def with_has_call(
Expand Down
15 changes: 7 additions & 8 deletions flake8_async/visitors/visitor91x.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,17 +501,16 @@ def _checkpoint_with(self, node: cst.With):
"""
if getattr(node, "asynchronous", None):
for item in node.items:
if not isinstance(item.item, cst.Call) or not isinstance(
item.item.func, (cst.Attribute, cst.Name)
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

func = identifier_to_string(item.item.func)
assert func is not None
if func not in ("trio.open_nursery", "anyio.create_task_group"):
self.checkpoint()
break
else:
self.uncheckpointed_statements = set()

Expand Down
7 changes: 3 additions & 4 deletions flake8_async/visitors/visitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import ast
from typing import TYPE_CHECKING, Any, cast

import libcst as cst

from .flake8asyncvisitor import Flake8AsyncVisitor, Flake8AsyncVisitor_cst
from .helpers import (
disabled_by_default,
Expand All @@ -20,6 +18,8 @@
if TYPE_CHECKING:
from collections.abc import Mapping

import libcst as cst

LIBRARIES = ("trio", "anyio", "asyncio")


Expand Down Expand Up @@ -460,8 +460,7 @@ def visit_CompIf(self, node: cst.CSTNode):

def visit_Call(self, node: cst.Call):
if (
isinstance(node.func, (cst.Name, cst.Attribute))
and identifier_to_string(node.func) == "asyncio.create_task"
identifier_to_string(node.func) == "asyncio.create_task"
and not self.safe_to_create_task
):
self.error(node)
Expand Down
5 changes: 5 additions & 0 deletions tests/autofix_files/async100.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,8 @@ async def nursery_no_cancel_point():
async def dont_crash_on_non_name_or_attr_call():
async with contextlib.asynccontextmanager(agen_fn)():
...


async def another_weird_with_call():
async with a().b():
...
5 changes: 5 additions & 0 deletions tests/eval_files/async100.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,8 @@ async def nursery_no_cancel_point():
async def dont_crash_on_non_name_or_attr_call():
async with contextlib.asynccontextmanager(agen_fn)():
...


async def another_weird_with_call():
async with a().b():
...
Loading