Skip to content

Commit 3187066

Browse files
authored
also check for kwargs in async115 and async116 (#384)
* also check for kwargs in async115 and async116
1 parent af6f28f commit 3187066

File tree

7 files changed

+68
-32
lines changed

7 files changed

+68
-32
lines changed

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Changelog
44

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

7+
25.5.3
8+
======
9+
- :ref:`ASYNC115 <async115>` and :ref:`ASYNC116 <async116>` now also checks kwargs.
10+
711
25.5.2
812
======
913
- :ref:`ASYNC102 <async102>` and :ref:`ASYNC120 <async120>` no longer requires cancel scopes to have a timeout. `(issue #272) <https://github.com/python-trio/flake8-async/issues/272>`_

docs/rules.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ _`ASYNC113` : start-soon-in-aenter
6565
_`ASYNC114` : startable-not-in-config
6666
Startable function (i.e. has a ``task_status`` keyword parameter) not in :ref:`--startable-in-context-manager <--startable-in-context-manager>` parameter list, please add it so ASYNC113 can catch errors when using it.
6767

68-
ASYNC115 : async-zero-sleep
68+
_`ASYNC115` : async-zero-sleep
6969
Replace :func:`trio.sleep(0) <trio.sleep>`/:func:`anyio.sleep(0) <anyio.sleep>` with the more suggestive :func:`trio.lowlevel.checkpoint`/:func:`anyio.lowlevel.checkpoint`.
7070

71-
ASYNC116 : long-sleep-not-forever
71+
_`ASYNC116` : long-sleep-not-forever
7272
:func:`trio.sleep`/:func:`anyio.sleep` with >24 hour interval should usually be :func:`trio.sleep_forever`/:func:`anyio.sleep_forever`.
7373

74-
ASYNC118 : cancelled-class-saved
74+
_`ASYNC118` : cancelled-class-saved
7575
Don't assign the value of :func:`anyio.get_cancelled_exc_class` to a variable, since that breaks linter checks and multi-backend programs.
7676

7777
_`ASYNC119` : yield-in-cm-in-async-gen

docs/usage.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ adding the following to your ``.pre-commit-config.yaml``:
3333
minimum_pre_commit_version: '2.9.0'
3434
repos:
3535
- repo: https://github.com/python-trio/flake8-async
36-
rev: 25.5.2
36+
rev: 25.5.3
3737
hooks:
3838
- id: flake8-async
3939
# args: ["--enable=ASYNC100,ASYNC112", "--disable=", "--autofix=ASYNC"]

flake8_async/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939

4040
# CalVer: YY.month.patch, e.g. first release of July 2022 == "22.7.1"
41-
__version__ = "25.5.2"
41+
__version__ = "25.5.3"
4242

4343

4444
# taken from https://github.com/Zac-HD/shed

flake8_async/visitors/visitors.py

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,16 @@ class Visitor115(Flake8AsyncVisitor):
305305
}
306306

307307
def visit_Call(self, node: ast.Call):
308+
if not (m := get_matching_call(node, "sleep")):
309+
return
308310
if (
309-
(m := get_matching_call(node, "sleep"))
310-
and len(node.args) == 1
311+
len(node.args) == 1
311312
and isinstance(node.args[0], ast.Constant)
312313
and node.args[0].value == 0
314+
) or (
315+
len(node.keywords) == 1
316+
and isinstance(node.keywords[0].value, ast.Constant)
317+
and node.keywords[0].value.value == 0
313318
):
314319
self.error(node, m.base)
315320

@@ -324,32 +329,40 @@ class Visitor116(Flake8AsyncVisitor):
324329
}
325330

326331
def visit_Call(self, node: ast.Call):
327-
if (m := get_matching_call(node, "sleep")) and len(node.args) == 1:
332+
if not (m := get_matching_call(node, "sleep")):
333+
return
334+
if len(node.args) == 1:
328335
arg = node.args[0]
329-
if (
330-
# `trio.sleep(math.inf)`
331-
(isinstance(arg, ast.Attribute) and arg.attr == "inf")
332-
# `trio.sleep(inf)`
333-
or (isinstance(arg, ast.Name) and arg.id == "inf")
334-
# `trio.sleep(float("inf"))`
335-
or (
336-
isinstance(arg, ast.Call)
337-
and isinstance(arg.func, ast.Name)
338-
and arg.func.id == "float"
339-
and len(arg.args)
340-
and isinstance(arg.args[0], ast.Constant)
341-
and arg.args[0].value == "inf"
342-
)
343-
# `trio.sleep(1e999)` (constant value inf)
344-
# `trio.sleep(86401)`
345-
# `trio.sleep(86400.1)`
346-
or (
347-
isinstance(arg, ast.Constant)
348-
and isinstance(arg.value, (int, float))
349-
and arg.value > 86400
350-
)
351-
):
352-
self.error(node, m.base)
336+
elif len(node.keywords) == 1:
337+
arg = node.keywords[0].value
338+
else:
339+
# invalid call, not our problem
340+
return
341+
342+
if (
343+
# `trio.sleep(math.inf)`
344+
(isinstance(arg, ast.Attribute) and arg.attr == "inf")
345+
# `trio.sleep(inf)`
346+
or (isinstance(arg, ast.Name) and arg.id == "inf")
347+
# `trio.sleep(float("inf"))`
348+
or (
349+
isinstance(arg, ast.Call)
350+
and isinstance(arg.func, ast.Name)
351+
and arg.func.id == "float"
352+
and len(arg.args)
353+
and isinstance(arg.args[0], ast.Constant)
354+
and arg.args[0].value == "inf"
355+
)
356+
# `trio.sleep(1e999)` (constant value inf)
357+
# `trio.sleep(86401)`
358+
# `trio.sleep(86400.1)`
359+
or (
360+
isinstance(arg, ast.Constant)
361+
and isinstance(arg.value, (int, float))
362+
and arg.value > 86400
363+
)
364+
):
365+
self.error(node, m.base)
353366

354367

355368
@error_class

tests/eval_files/async115.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ async def afoo():
2222
time.sleep(0)
2323
sleep(0)
2424

25+
# in trio it's called 'seconds', in anyio it's 'delay', but
26+
# we don't care about the kwarg name. #382
27+
await trio.sleep(seconds=0) # error: 10, "trio"
28+
await trio.sleep(delay=0) # error: 10, "trio"
29+
await trio.sleep(anything=0) # error: 10, "trio"
30+
31+
await trio.sleep(seconds=1)
32+
33+
await trio.sleep()
34+
35+
# we don't care to suppress this
36+
await trio.sleep(0, seconds=1) # error: 10, "trio"
37+
2538

2639
# don't require being inside a function
2740
trio.sleep(0) # error: 0, "trio"

tests/eval_files/async116.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ async def foo():
4242
await trio.sleep(inf.inf) # error: 10, "trio"
4343
await trio.sleep(inf.anything)
4444

45+
# in trio the kwarg name is 'seconds', in anyio it's 'delay', but
46+
# we error regardless of what it is. #382
47+
await trio.sleep(seconds=inf) # error: 10, "trio"
48+
await trio.sleep(delay=inf) # error: 10, "trio"
49+
await trio.sleep(anything=inf) # error: 10, "trio"
50+
4551

4652
# does not require the call to be awaited, nor in an async fun
4753
trio.sleep(86401) # error: 0, "trio"

0 commit comments

Comments
 (0)