Skip to main content

A highly opinionated flake8 plugin for Trio-related problems.

Project description


A highly opinionated flake8 plugin for Trio-related problems.

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-async and flake8-bugbear.


pip install flake8-trio

List of warnings

  • TRIO100: a with trio.fail_after(...): or with trio.move_on_after(...): context does not contain any await statements. This makes it pointless, as the timeout can only be triggered by a checkpoint.
  • TRIO101: yield inside a nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling.
  • TRIO102: it's unsafe to await inside finally: or except BaseException/trio.Cancelled unless you use a shielded cancel scope with a timeout.
  • TRIO103: except BaseException and except trio.Cancelled with a code path that doesn't re-raise.
  • TRIO104: Cancelled and BaseException must be re-raised - when a user tries to return or raise a different exception.
  • TRIO105: Calling a trio async function without immediately awaiting it.
  • TRIO106: trio must be imported with import trio for the linter to work.
  • TRIO107: exit or return from async function with no guaranteed checkpoint or exception since function definition.
  • TRIO108: 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).
  • TRIO109: Async function definition with a timeout parameter - use trio.[fail/move_on]_[after/at] instead
  • TRIO110: while <condition>: await trio.sleep() should be replaced by a trio.Event.
  • TRIO111: Variable, from context manager opened inside nursery, passed to start[_soon] might be invalidly accesed 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.
  • TRIO112: 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.
  • TRIO113: using nursery.start_soon in __aenter__ doesn't wait for the task to begin. Consider replacing with nursery.start.
  • TRIO114: Startable function (i.e. has a task_status parameter) not in --startable-in-context-manager parameter list, please add it so TRIO113 can catch errors when using it.
  • TRIO115: Replace trio.sleep(0) with the more suggestive trio.lowlevel.checkpoint().
  • TRIO116: trio.sleep() with >24 hour interval should usually betrio.sleep_forever().
  • TRIO200: User-configured error for blocking sync calls in async functions. Does nothing by default, see trio200-blocking-calls for how to configure it.


You can configure flake8 with command-line options, 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.


Specify a list of decorators to disable checkpointing checks for, turning off TRIO107 and TRIO108 warnings for functions decorated with any decorator matching any in the list. Matching is done with fnmatch. 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. matches,, and, here), etc.

For example:

no-checkpoint-warning-decorators =


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 =


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 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.


trio200-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 parantheses, and will only match when the pattern is the name of a call, so given the above configuration

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



CalVer, YY.month.patch


  • The --startable-in-context-manager and --trio200-blocking-calls options now handle spaces and newlines.
  • Now compatible with flake8-noqa's NQA102 and NQA103 checks.


  • TRIO200 no longer warns on directly awaited calls


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


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


  • 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).


  • Add TRIO116: trio.sleep() with >24 hour interval should usually be trio.sleep_forever().


  • Add TRIO114 Startable function not in --startable-in-context-manager parameter list.


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


  • TRIO105 now also checks that you awaited nursery.start().


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


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


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


  • Fix false alarm on TRIO107 with checkpointing try and empty finally
  • Fix false alarm on TRIO107&108 with infinite loops


  • TRIO107+108 now ignores asynccontextmanagers, 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.


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


  • Add TRIO111: Variable, from context manager opened inside nursery, passed to start[_soon] might be invalidly accesed 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.


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


  • TRIO108 now gives multiple error messages; one for each path lacking a guaranteed checkpoint


  • Merged TRIO108 into TRIO107
  • TRIO108 now handles checkpointing in async iterators


  • 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.


  • 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


  • 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.


  • Added TRIO105 check for not immediately awaiting async trio functions.
  • Added TRIO106 check that trio is imported in a form that the plugin can easily parse.


  • Added TRIO102 check for unsafe checkpoints inside finally: blocks


  • Avoid TRIO100 false-alarms on cancel scopes containing async for or async with.


  • Initial release with TRIO100 and TRIO101

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flake8-trio-22.12.5.tar.gz (39.8 kB view hashes)

Uploaded source

Built Distribution

flake8_trio-22.12.5-py3-none-any.whl (19.1 kB view hashes)

Uploaded py3

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring Fastly Fastly CDN Google Google Object Storage and Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page