Metadata-Version: 2.1
Name: flake8-async
Version: 24.3.2
Summary: A highly opinionated flake8 plugin for Trio-related problems.
Home-page: https://github.com/python-trio/flake8-async
Author: Zac Hatfield-Dodds, John Litborn, and Contributors
Author-email: zac@zhd.dev
License: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Flake8
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flake8>=6
Requires-Dist: libcst>=1.0.1

[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/python-trio/flake8-async/main.svg)](https://results.pre-commit.ci/latest/github/python-trio/flake8-async/main)
[![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)
# flake8-async

A highly opinionated flake8 plugin for problems related to [Trio](https://github.com/python-trio/trio), [AnyIO](https://github.com/agronholm/anyio), or [asyncio](https://docs.python.org/3/library/asyncio.html).

This can include anything from outright bugs, to pointless/dead code,
to likely performance issues, to minor points of idiom that might signal
a misunderstanding.

It may well be too noisy for anyone with different opinions, that's OK.

Pairs well with flake8-bugbear.

Some checks are incorporated into [ruff](https://github.com/astral-sh/ruff).

This plugin was previously known as flake8-trio, and there was a separate small plugin known as flake8-async for asyncio. But this plugin was a superset of the checks in flake8-async, and support for anyio was added, so it's now named flake8-async to more properly convey its usage. At the same time all error codes were renamed from TRIOxxx to ASYNCxxx, as was previously used by the old flake8-async.

## Installation

```console
pip install flake8-async
```

## List of warnings
- **ASYNC100**: A `with [trio/anyio].fail_after(...):` or `with [trio/anyio].move_on_after(...):`
  context does not contain any `await` statements.  This makes it pointless, as
  the timeout can only be triggered by a checkpoint.
- **ASYNC101**: `yield` inside a trio/anyio nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling.
- **ASYNC102**: It's unsafe to await inside `finally:` or `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError` unless you use a shielded cancel scope with a timeout. This is currently not able to detect asyncio shields.
- **ASYNC103**: `except BaseException/trio.Cancelled/anyio.get_cancelled_exc_class()/asyncio.exceptions.CancelledError`, or a bare `except:` with a code path that doesn't re-raise. If you don't want to re-raise `BaseException`, add a separate handler for `trio.Cancelled`/`anyio.get_cancelled_exc_class()`/`asyncio.exceptions.CancelledError` before.
- **ASYNC104**: `trio.Cancelled`/`anyio.get_cancelled_exc_class()`/`asyncio.exceptions.CancelledError`/`BaseException` must be re-raised. The same as ASYNC103, except specifically triggered on `return` or a different exception being raised.
- **ASYNC105**: Calling a trio async function without immediately `await`ing it. This is only supported with trio functions, but you can get similar functionality with a type-checker.
- **ASYNC106**: `trio`/`anyio`/`asyncio` must be imported with `import trio`/`import anyio`/`import asyncio` for the linter to work.
- **ASYNC109**: Async function definition with a `timeout` parameter - use `[trio/anyio].[fail/move_on]_[after/at]` instead.
- **ASYNC110**: `while <condition>: await [trio/anyio].sleep()` should be replaced by a `[trio/anyio].Event`.
- **ASYNC111**: Variable, from context manager opened inside nursery, passed to `start[_soon]` might be invalidly accessed while in use, due to context manager closing before the nursery. This is usually a bug, and nurseries should generally be the inner-most context manager.
- **ASYNC112**: Nursery body with only a call to `nursery.start[_soon]` and not passing itself as a parameter can be replaced with a regular function call.
- **ASYNC113**: Using `nursery.start_soon` in `__aenter__` doesn't wait for the task to begin. Consider replacing with `nursery.start`.
- **ASYNC114**: Startable function (i.e. has a `task_status` keyword parameter) not in `--startable-in-context-manager` parameter list, please add it so ASYNC113 can catch errors when using it.
- **ASYNC115**: Replace `[trio/anyio].sleep(0)` with the more suggestive `[trio/anyio].lowlevel.checkpoint()`.
- **ASYNC116**: `[trio/anyio].sleep()` with >24 hour interval should usually be `[trio/anyio].sleep_forever()`.
- **ASYNC118**: Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs.

### Warnings for blocking sync calls in async functions
Note: 22X, 23X and 24X has not had asyncio-specific suggestions written.
- **ASYNC200**: User-configured error for blocking sync calls in async functions. Does nothing by default, see [`async200-blocking-calls`](#async200-blocking-calls) for how to configure it.
- **ASYNC210**: Sync HTTP call in async function, use `httpx.AsyncClient`. This and the other ASYNC21x checks look for usage of `urllib3` and `httpx.Client`, and recommend using `httpx.AsyncClient` as that's the largest http client supporting anyio/trio.
- **ASYNC211**: Likely sync HTTP call in async function, use `httpx.AsyncClient`. Looks for `urllib3` method calls on pool objects, but only matching on the method signature and not the object.
- **ASYNC212**: Blocking sync HTTP call on httpx object, use httpx.AsyncClient.
- **ASYNC220**: Sync process call in async function, use `await nursery.start([trio/anyio].run_process, ...)`. `asyncio` users can use [`asyncio.create_subprocess_[exec/shell]`](https://docs.python.org/3/library/asyncio-subprocess.html).
- **ASYNC221**: Sync process call in async function, use `await [trio/anyio].run_process(...)`. `asyncio` users can use [`asyncio.create_subprocess_[exec/shell]`](https://docs.python.org/3/library/asyncio-subprocess.html).
- **ASYNC222**: Sync `os.*` call in async function, wrap in `await [trio/anyio].to_thread.run_sync()`. `asyncio` users can use [`asyncio.loop.run_in_executor`](https://docs.python.org/3/library/asyncio-subprocess.html).
- **ASYNC230**: Sync IO call in async function, use `[trio/anyio].open_file(...)`. `asyncio` users need to use a library such as [aiofiles](https://pypi.org/project/aiofiles/), or switch to [anyio](https://github.com/agronholm/anyio).
- **ASYNC231**: Sync IO call in async function, use `[trio/anyio].wrap_file(...)`. `asyncio` users need to use a library such as [aiofiles](https://pypi.org/project/aiofiles/), or switch to [anyio](https://github.com/agronholm/anyio).
- **ASYNC232**: Blocking sync call on file object, wrap the file object in `[trio/anyio].wrap_file()` to get an async file object.
- **ASYNC240**: Avoid using `os.path` in async functions, prefer using `[trio/anyio].Path` objects. `asyncio` users should consider [aiopath](https://pypi.org/project/aiopath) or [anyio](https://github.com/agronholm/anyio).
- **ASYNC250**: Builtin `input()` should not be called from async function.

### Warnings disabled by default
- **ASYNC900**: Async generator without `@asynccontextmanager` not allowed. You might want to enable this on a codebase since async generators are inherently unsafe and cleanup logic might not be performed. See https://github.com/python-trio/flake8-async/issues/211 and https://discuss.python.org/t/using-exceptiongroup-at-anthropic-experience-report/20888/6 for discussion.
- **ASYNC910**: Exit or `return` from async function with no guaranteed checkpoint or exception since function definition. You might want to enable this on a codebase to make it easier to reason about checkpoints, and make the logic of ASYNC911 correct.
- **ASYNC911**: Exit, `yield` or `return` from async iterable with no guaranteed checkpoint since possible function entry (yield or function definition)
  Checkpoints are `await`, `async for`, and `async with` (on one of enter/exit).

### Removed Warnings
- **TRIOxxx**: All error codes are now renamed ASYNCxxx
- **TRIO107**: Renamed to TRIO910
- **TRIO108**: Renamed to TRIO911
- **TRIO117**: Don't raise or catch `trio.[NonBase]MultiError`, prefer `[exceptiongroup.]BaseExceptionGroup`. `MultiError` was removed in trio==0.24.0.

## Examples
### install and run through flake8
```sh
pip install flake8 flake8-async
flake8 .
```
### install and run with pre-commit
If you use [pre-commit](https://pre-commit.com/), you can use it with flake8-async by
adding the following to your `.pre-commit-config.yaml`:

```yaml
minimum_pre_commit_version: '2.9.0'
repos:
- repo: https://github.com/python-trio/flake8-async
  rev: 24.3.2
  hooks:
    - id: flake8-async
      # args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC]
```

This is often considerably faster for large projects, because `pre-commit`
can avoid running `flake8-async` on unchanged files.


Afterwards, run
```sh
pip install pre-commit flake8-async
pre-commit run .
```
### install and run as standalone
If inside a git repository, running without arguments will run it against all `*.py` files in the repository.
```sh
pip install flake8-async
flake8-async
```
#### with autofixes
```sh
flake8-async --autofix=ASYNC
```
#### specifying source files
```sh
flake8-async my_python_file.py
```
##### zsh-only
```zsh
flake8-async **/*.py
```

## Configuration
[You can configure `flake8` with command-line options](https://flake8.pycqa.org/en/latest/user/configuration.html),
but we prefer using a config file. The file needs to start with a section marker `[flake8]` and the following options are then parsed using flake8's config parser, and can be used just like any other flake8 options.
Note that it's not currently possible to use a configuration file when running `flake8-async` standalone.

### `--enable`
Comma-separated list of error codes to enable, similar to flake8 --select but is additionally more performant as it will disable non-enabled visitors from running instead of just silencing their errors.

### `--disable`
Comma-separated list of error codes to disable, similar to flake8 --ignore but is additionally more performant as it will disable non-enabled visitors from running instead of just silencing their errors.

### `--autofix`
Comma-separated list of error-codes to enable autofixing for if implemented. Requires running as a standalone program. Pass `--autofix=ASYNC` to enable all autofixes.

### `--error-on-autofix`
Whether to also print an error message for autofixed errors.

### `--anyio`
Change the default library to be anyio instead of trio. If trio is imported it will assume both are available and print suggestions with [anyio/trio].

### `no-checkpoint-warning-decorators`
Comma-separated list of decorators to disable checkpointing checks for, turning off ASYNC910 and ASYNC911 warnings for functions decorated with any decorator matching any in the list. Matching is done with [fnmatch](https://docs.python.org/3/library/fnmatch.html). Defaults to disabling for `asynccontextmanager`.

Decorators-to-match must be identifiers or dotted names only (not PEP-614 expressions), and will match against the name only - e.g. `foo.bar` matches `foo.bar`, `foo.bar()`, and `foo.bar(args, here)`, etc.

For example:
```
no-checkpoint-warning-decorators =
  mydecorator,
  mydecoratorpackage.checkpointing_decorators.*,
  ign*,
  *.ignore,
```


### `startable-in-context-manager`
Comma-separated list of methods which should be used with `.start()` when opening a context manager,
in addition to the default `trio.run_process`, `trio.serve_tcp`, `trio.serve_ssl_over_tcp`, and
`trio.serve_listeners`.  Names must be valid identifiers as per `str.isidentifier()`. For example:
```
startable-in-context-manager =
  myfun,
  myfun2,
```

### `async200-blocking-calls`
Comma-separated list of pairs of values separated by `->` (optional whitespace stripped), where the first is a pattern for a call that should raise an error if found inside an async function, and the second is what should be suggested to use instead. It uses fnmatch as per [`no-checkpoint-warning-decorators`](#no-checkpoint-warning-decorators) for matching. The part after `->` is not used by the checker other than when printing the error, so you could add extra info there if you want.

The format of the error message is `User-configured blocking sync call {0} in async function, consider replacing with {1}.`, where `{0}` is the pattern the call matches and `{1}` is the suggested replacement.

Example:
```ini
async200-blocking-calls =
  my_blocking_call -> async.alternative,
  module.block_call -> other_function_to_use,
  common_error_call -> alternative(). But sometimes you should use other_function(). Ask joe if you're unsure which one,
  dangerous_module.* -> corresponding function in safe_module,
  *.dangerous_call -> .safe_call()
```
Specified patterns must not have parentheses, and will only match when the pattern is the name of a call, so given the above configuration
```python
async def my_function():
    my_blocking_call()  # this would raise an error
    x = my_blocking_call(a, b, c)  # as would this
    y = my_blocking_call  # but not this
    y()  # or this
    [my_blocking_call][0]()  # nor this

    def my_blocking_call():  # it's also safe to use the name in other contexts
        ...

    arbitrary_other_function(my_blocking_call=None)
```


# Changelog
*[CalVer, YY.month.patch](https://calver.org/)*

## 24.3.2
- Add ASYNC250: blocking sync call `input()` in async method.

## 24.3.1
- Removed TRIO117, MultiError removed in trio 0.24.0
- Renamed the library from flake8-trio to flake8-async, to indicate the checker supports more than just `trio`.
- Renamed all error codes from TRIOxxx to ASYNCxxx
- Renamed the binary from flake8-trio to flake8-async
- Lots of internal renaming.
- Added asyncio support for several error codes
- added `--library`

## 23.5.1
- TRIO91X now supports comprehensions
- TRIO100 and TRIO91X now supports autofixing
- Renamed `--enable-visitor-codes-regex` to `--enable`
- Added `--disable`, `--autofix` and `--error-on-autofix`

## 23.2.5
- Fix false alarms for `@pytest.fixture`-decorated functions in TRIO101, TRIO910 and TRIO911

## 23.2.4
- Fix TRIO900 false alarm on nested functions
- TRIO113 now also works on `anyio.TaskGroup`

## 23.2.3
- Fix get_matching_call when passed a single string as base. Resolves possibly several false alarms, TRIO210 among them.

## 23.2.2
- Rename TRIO107 to TRIO910, and TRIO108 to TRIO911, and making them optional by default.
- Allow `@pytest.fixture()`-decorated async generators, since they're morally context managers
- Add support for checking code written against [`anyio`](https://anyio.readthedocs.io/en/stable/)
- Add TRIO118: Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs.

## 23.2.1
- TRIO103 and TRIO104 no longer triggers when `trio.Cancelled` has been handled in previous except handlers.
- Add TRIO117: Reference to deprecated `trio.[NonBase]MultiError`; use `[Base]ExceptionGroup` instead.
- Add TRIO232: blocking sync call on file object.
- Add TRIO212: blocking sync call on `httpx.Client` object.
- Add TRIO222: blocking sync call to `os.wait*`
- TRIO221 now also looks for `os.posix_spawn[p]`

## 23.1.4
- TRIO114 avoids a false alarm on posonly args named "task_status"
- TRIO116 will now match on any attribute parameter named `.inf`, not just `math.inf`.
- TRIO900 now only checks `@asynccontextmanager`, not other decorators passed with --no-checkpoint-warning-decorators.

## 23.1.3
- Add TRIO240: usage of `os.path` in async function.
- Add TRIO900: ban async generators not decorated with known safe decorator

## 23.1.2
- Add TRIO230, TRIO231 - sync IO calls in async function

## 23.1.1
- Add TRIO210, TRIO211 - blocking sync call in async function, using network packages (requests, httpx, urllib3)
- Add TRIO220, TRIO221 - blocking sync call in async function, using subprocess or os.

## 22.12.5
- The `--startable-in-context-manager` and `--trio200-blocking-calls` options now handle spaces and newlines.
- Now compatible with  [flake8-noqa](https://pypi.org/project/flake8-noqa/)'s NQA102 and NQA103 checks.

## 22.12.4
- TRIO200 no longer warns on directly awaited calls

## 22.12.3
- Worked around configuration-parsing bug for TRIO200 warning (more to come)

## 22.12.2
- Add TRIO200: User-configured blocking sync call  in async function

## 22.12.1
- TRIO114 will now trigger on the unqualified name, will now only check the first parameter
  directly, and parameters to function calls inside that.
- TRIO113 now only supports names that are valid identifiers, rather than fnmatch patterns.
- Add TRIO115: Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`.

## 22.11.5
- Add TRIO116: `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()`.

## 22.11.4
- Add TRIO114 Startable function not in `--startable-in-context-manager` parameter list.

## 22.11.3
- Add TRIO113, prefer `await nursery.start(...)` to `nursery.start_soon()` for compatible functions when opening a context manager

## 22.11.2
- TRIO105 now also checks that you `await`ed `nursery.start()`.

## 22.11.1
- TRIO102 is no longer skipped in (async) context managers, since it's not a missing-checkpoint warning.

## 22.9.2
- Fix a crash on nontrivial decorator expressions (calls, PEP-614) and document behavior.

## 22.9.1
- Add `--no-checkpoint-warning-decorators` option, to disable missing-checkpoint warnings for certain decorated functions.

## 22.8.8
- Fix false alarm on TRIO107 with checkpointing `try` and empty `finally`
- Fix false alarm on TRIO107&108 with infinite loops

## 22.8.7
- TRIO107+108 now ignores `asynccontextmanager`s, since both `__aenter__` and `__aexit__` should checkpoint. `async with` is also treated as checkpointing on both enter and exit.
- TRIO107 now completely ignores any function whose body consists solely of ellipsis, pass, or string constants.
- TRIO103, 107 and 108 now inspects `while` conditions and `for` iterables to avoid false alarms on a couple cases where the loop body is guaranteed to run at least once.

## 22.8.6
- TRIO103 now correctly handles raises in loops, i.e. `raise` in else is guaranteed to run unless there's a `break` in the body.

## 22.8.5
- Add TRIO111: Variable, from context manager opened inside nursery, passed to `start[_soon]` might be invalidly accessed while in use, due to context manager closing before the nursery. This is usually a bug, and nurseries should generally be the inner-most context manager.
- Add TRIO112: this single-task nursery could be replaced by awaiting the function call directly.

## 22.8.4
- Fix TRIO108 raising errors on yields in some sync code.
- TRIO109 now skips all decorated functions to avoid false alarms

## 22.8.3
- TRIO108 now gives multiple error messages; one for each path lacking a guaranteed checkpoint

## 22.8.2
- Merged TRIO108 into TRIO107
- TRIO108 now handles checkpointing in async iterators

## 22.8.1
- Added TRIO109: Async definitions should not have a `timeout` parameter. Use `trio.[fail/move_on]_[at/after]`
- Added TRIO110: `while <condition>: await trio.sleep()` should be replaced by a `trio.Event`.

## 22.7.6
- Extend TRIO102 to also check inside `except BaseException` and `except trio.Cancelled`
- Extend TRIO104 to also check for `yield`
- Update error messages on TRIO102 and TRIO103

## 22.7.5
- Add TRIO103: `except BaseException` or `except trio.Cancelled` with a code path that doesn't re-raise
- Add TRIO104: "Cancelled and BaseException must be re-raised" if user tries to return or raise a different exception.
- Added TRIO107: Async functions must have at least one checkpoint on every code path, unless an exception is raised
- Added TRIO108: Early return from async function must have at least one checkpoint on every code path before it.

## 22.7.4
- Added TRIO105 check for not immediately `await`ing async trio functions.
- Added TRIO106 check that trio is imported in a form that the plugin can easily parse.

## 22.7.3
- Added TRIO102 check for unsafe checkpoints inside `finally:` blocks

## 22.7.2
- Avoid `TRIO100` false-alarms on cancel scopes containing `async for` or `async with`.

## 22.7.1
- Initial release with TRIO100 and TRIO101
