Skip to content

Add trio[300], async functions should not have a timeout parameter #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 5, 2022
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog
*[CalVer, YY.month.patch](https://calver.org/)*

## Future
- Added TRIO300: Async definitions should not have a `timeout` parameter. Use `trio.[fail/move_on]_[at/after]`

## 22.7.6
- Extend TRIO102 to also check inside `except BaseException` and `except trio.Cancelled`
- Extend TRIO104 to also check for `yield`
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ pip install flake8-trio
- **TRIO107**: Async functions must have at least one checkpoint on every code path, unless an exception is raised.
- **TRIO108**: Early return from async function must have at least one checkpoint on every code path before it, unless an exception is raised.
Checkpoints are `await`, `async with` `async for`.
- **TRIO300**: Async function definition with a `timeout` parameter - use `trio.[fail/move_on]_[after/at]` instead
7 changes: 7 additions & 0 deletions flake8_trio.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def visit_FunctionDef(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef]):
self._safe_decorator, self._yield_is_error = outer

def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef):
self.check_300(node.args)
self.visit_FunctionDef(node)

def visit_Yield(self, node: ast.Yield):
Expand Down Expand Up @@ -187,6 +188,11 @@ def visit_Import(self, node: ast.Import):
if name.name == "trio" and name.asname is not None:
self.problems.append(make_error(TRIO106, node.lineno, node.col_offset))

def check_300(self, args: ast.arguments):
for arg in (*args.posonlyargs, *args.args, *args.kwonlyargs):
if arg.arg == "timeout":
self.error(TRIO300, arg.lineno, arg.col_offset)


def critical_except(node: ast.ExceptHandler) -> Optional[Tuple[int, int, str]]:
def has_exception(node: Optional[ast.expr]) -> str:
Expand Down Expand Up @@ -629,3 +635,4 @@ def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]:
TRIO106 = "TRIO106: trio must be imported with `import trio` for the linter to work"
TRIO107 = "TRIO107: Async functions must have at least one checkpoint on every code path, unless an exception is raised"
TRIO108 = "TRIO108: Early return from async function must have at least one checkpoint on every code path before it."
TRIO300 = "TRIO300: Async function definition with a `timeout` parameter - use `trio.[fail/move_on]_[after/at]` instead"
78 changes: 78 additions & 0 deletions tests/trio300.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
timeout = 10


async def foo():
...


# args
async def foo_1(timeout): # error: 16
...


# arg in args wih default & annotation
async def foo_2(timeout: int = 3): # error: 16
...


# vararg
async def foo_3(*timeout): # ignored
...


# kwarg
async def foo_4(**timeout): # ignored
...


# correct line/col
async def foo_5(
bar,
timeouts,
my_timeout,
timeout_,
timeout, # error: 4
):
...


# posonlyargs
async def foo_6(
timeout, # error: 4
/,
bar,
):
...


# kwonlyargs
async def foo_7(
*,
timeout, # error: 4
):
...


# kwonlyargs (and kw_defaults)
async def foo_8(
*,
timeout=5, # error: 4
):
...


async def foo_9(k=timeout):
...


# normal functions are not checked
def foo_10(timeout):
...


def foo_11(timeout, /):
...


def foo_12(*, timeout):
...