Skip to content

Commit

Permalink
fix crash. Make identifier_to_string less footgunny. (#323)
Browse files Browse the repository at this point in the history
* fix crash. Make identifier_to_string less footgunny.

* add changelog & bump version. Fix incorrect changelog placement.
  • Loading branch information
jakkdl authored Nov 18, 2024
1 parent 7a45176 commit 14d9906
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 24 deletions.
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():
...

0 comments on commit 14d9906

Please sign in to comment.