diff --git a/docs/changelog.rst b/docs/changelog.rst
index 9f8cc5e..26b10c5 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,10 @@ Changelog
`CalVer, YY.month.patch `_
+25.5.3
+======
+- :ref:`ASYNC115 ` and :ref:`ASYNC116 ` now also checks kwargs.
+
25.5.2
======
- :ref:`ASYNC102 ` and :ref:`ASYNC120 ` no longer requires cancel scopes to have a timeout. `(issue #272) `_
diff --git a/docs/usage.rst b/docs/usage.rst
index a3101c5..b1bb6c2 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -33,7 +33,7 @@ adding the following to your ``.pre-commit-config.yaml``:
minimum_pre_commit_version: '2.9.0'
repos:
- repo: https://github.com/python-trio/flake8-async
- rev: 25.5.2
+ rev: 25.5.3
hooks:
- id: flake8-async
# args: ["--enable=ASYNC100,ASYNC112", "--disable=", "--autofix=ASYNC"]
diff --git a/flake8_async/__init__.py b/flake8_async/__init__.py
index 15583f6..c0ba7b3 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__ = "25.5.2"
+__version__ = "25.5.3"
# taken from https://github.com/Zac-HD/shed
diff --git a/flake8_async/visitors/visitors.py b/flake8_async/visitors/visitors.py
index f77f2e6..e4fa1c8 100644
--- a/flake8_async/visitors/visitors.py
+++ b/flake8_async/visitors/visitors.py
@@ -305,11 +305,16 @@ class Visitor115(Flake8AsyncVisitor):
}
def visit_Call(self, node: ast.Call):
+ if not (m := get_matching_call(node, "sleep")):
+ return
if (
- (m := get_matching_call(node, "sleep"))
- and len(node.args) == 1
+ len(node.args) == 1
and isinstance(node.args[0], ast.Constant)
and node.args[0].value == 0
+ ) or (
+ len(node.keywords) == 1
+ and isinstance(node.keywords[0].value, ast.Constant)
+ and node.keywords[0].value.value == 0
):
self.error(node, m.base)
@@ -324,32 +329,40 @@ class Visitor116(Flake8AsyncVisitor):
}
def visit_Call(self, node: ast.Call):
- if (m := get_matching_call(node, "sleep")) and len(node.args) == 1:
+ if not (m := get_matching_call(node, "sleep")):
+ return
+ if len(node.args) == 1:
arg = node.args[0]
- if (
- # `trio.sleep(math.inf)`
- (isinstance(arg, ast.Attribute) and arg.attr == "inf")
- # `trio.sleep(inf)`
- or (isinstance(arg, ast.Name) and arg.id == "inf")
- # `trio.sleep(float("inf"))`
- or (
- isinstance(arg, ast.Call)
- and isinstance(arg.func, ast.Name)
- and arg.func.id == "float"
- and len(arg.args)
- and isinstance(arg.args[0], ast.Constant)
- and arg.args[0].value == "inf"
- )
- # `trio.sleep(1e999)` (constant value inf)
- # `trio.sleep(86401)`
- # `trio.sleep(86400.1)`
- or (
- isinstance(arg, ast.Constant)
- and isinstance(arg.value, (int, float))
- and arg.value > 86400
- )
- ):
- self.error(node, m.base)
+ elif len(node.keywords) == 1:
+ arg = node.keywords[0].value
+ else:
+ # invalid call, not our problem
+ return
+
+ if (
+ # `trio.sleep(math.inf)`
+ (isinstance(arg, ast.Attribute) and arg.attr == "inf")
+ # `trio.sleep(inf)`
+ or (isinstance(arg, ast.Name) and arg.id == "inf")
+ # `trio.sleep(float("inf"))`
+ or (
+ isinstance(arg, ast.Call)
+ and isinstance(arg.func, ast.Name)
+ and arg.func.id == "float"
+ and len(arg.args)
+ and isinstance(arg.args[0], ast.Constant)
+ and arg.args[0].value == "inf"
+ )
+ # `trio.sleep(1e999)` (constant value inf)
+ # `trio.sleep(86401)`
+ # `trio.sleep(86400.1)`
+ or (
+ isinstance(arg, ast.Constant)
+ and isinstance(arg.value, (int, float))
+ and arg.value > 86400
+ )
+ ):
+ self.error(node, m.base)
@error_class
diff --git a/tests/eval_files/async115.py b/tests/eval_files/async115.py
index fbfa7d9..05dd758 100644
--- a/tests/eval_files/async115.py
+++ b/tests/eval_files/async115.py
@@ -22,6 +22,19 @@ async def afoo():
time.sleep(0)
sleep(0)
+ # in trio it's called 'seconds', in anyio it's 'delay', but
+ # we don't care about the kwarg name. #382
+ await trio.sleep(seconds=0) # error: 10, "trio"
+ await trio.sleep(delay=0) # error: 10, "trio"
+ await trio.sleep(anything=0) # error: 10, "trio"
+
+ await trio.sleep(seconds=1)
+
+ await trio.sleep()
+
+ # we don't care to suppress this
+ await trio.sleep(0, seconds=1) # error: 10, "trio"
+
# don't require being inside a function
trio.sleep(0) # error: 0, "trio"
diff --git a/tests/eval_files/async116.py b/tests/eval_files/async116.py
index 60bf8b9..4602922 100644
--- a/tests/eval_files/async116.py
+++ b/tests/eval_files/async116.py
@@ -42,6 +42,12 @@ async def foo():
await trio.sleep(inf.inf) # error: 10, "trio"
await trio.sleep(inf.anything)
+ # in trio the kwarg name is 'seconds', in anyio it's 'delay', but
+ # we error regardless of what it is. #382
+ await trio.sleep(seconds=inf) # error: 10, "trio"
+ await trio.sleep(delay=inf) # error: 10, "trio"
+ await trio.sleep(anything=inf) # error: 10, "trio"
+
# does not require the call to be awaited, nor in an async fun
trio.sleep(86401) # error: 0, "trio"