***** Rules ***** General rules ============= Our ``ASYNC1xx`` rules check for semantic problems ranging from fatal errors (e.g. 101), to idioms for clearer code (e.g. 116). _`ASYNC100` : cancel-scope-no-checkpoint A :ref:`timeout_context` does not contain any :ref:`checkpoints `. This makes it pointless, as the timeout can only be triggered by a checkpoint. This check also treats ``yield`` as a checkpoint, since checkpoints can happen in the caller we yield to. :func:`trio.open_nursery` and :func:`anyio.create_task_group` are excluded, as they are :ref:`schedule points ` but not :ref:`cancel points ` (unless they have tasks started in them). See :ref:`ASYNC912 ` which will in addition guarantee checkpoints on every code path. _`ASYNC101` : yield-in-cancel-scope ``yield`` inside a :ref:`taskgroup_nursery` or :ref:`timeout_context` is only safe when implementing a context manager - otherwise, it breaks exception handling. See `this thread `_ for discussion of a future PEP. This has substantial overlap with :ref:`ASYNC119 `, which will warn on almost all instances of ASYNC101, but ASYNC101 is about a conceptually different problem that will not get resolved by :pep:`533`. Also triggered on common third-party context managers that open internal cancel scopes, nurseries, or task groups: ``trio_websocket.{open_websocket, open_websocket_url, serve_websocket}``, ``trio_asyncio.open_loop``, ``trio_parallel.open_worker_context``, ``trio_util.{move_on_when, run_and_cancelling}``, ``qtrio.{open_emissions_nursery, enter_emissions_channel}``, ``anyio.from_thread.{BlockingPortal, start_blocking_portal}``, ``asgi_lifespan.LifespanManager``, ``apscheduler.AsyncScheduler``, ``mcp.client.streamable_http.streamablehttp_client``, and ``mcp.client.sse.sse_client``. _`ASYNC102` : await-in-finally-or-cancelled ``await`` inside ``finally``, :ref:`cancelled-catching ` ``except:``, or ``__aexit__`` must have shielded :ref:`cancel scope ` with timeout. If not, the async call will immediately raise a new cancellation, suppressing any cancellation that was caught. Not applicable to asyncio due to edge-based cancellation semantics it uses as opposed to level-based used by trio and anyio. Calls to ``.aclose()`` (with no arguments) and to :func:`trio.aclose_forcefully` / :func:`anyio.aclose_forcefully` are exempt, as they are intended for use in cleanup. See :ref:`ASYNC120 ` for the general case where other exceptions might get suppressed. ASYNC103 : no-reraise-cancelled :ref:`cancelled`-catching exception that does not reraise the exception. If you don't want to re-raise :class:`BaseException`, add a separate handler for :ref:`Cancelled` before. ASYNC104 : cancelled-not-raised :ref:`Cancelled`-catching exception does not raise the exception. Triggered on ``return`` or raising a different exception. ASYNC105 : missing-await async trio function called without using ``await``. This is only supported with trio functions, but you can get similar functionality with a type-checker. _`ASYNC106` : bad-async-library-import trio/anyio/asyncio should be imported with ``import xxx`` for consistency. Opt-in style check; the linter resolves other import styles correctly. ASYNC109 : async-function-with-timeout Async function definition with a ``timeout`` parameter. In structured concurrency the caller should instead use :ref:`timeout context managers `. ASYNC110 : async-busy-wait ``while ...: await [trio/anyio].sleep()`` should be replaced by a :class:`trio.Event`/:class:`anyio.Event`. ASYNC111 : variable-from-cm-in-start-soon Variable, from context manager opened inside :ref:`taskgroup_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 : useless-nursery :ref:`taskgroup_nursery` body with only a call to ``.start[_soon]`` and not passing itself as a parameter can be replaced with a regular function call. _`ASYNC113` : start-soon-in-aenter Using :meth:`~trio.Nursery.start_soon`/:meth:`~anyio.abc.TaskGroup.start_soon` in ``__aenter__`` doesn't wait for the task to begin. Consider replacing with :meth:`~trio.Nursery.start`/:meth:`~anyio.abc.TaskGroup.start`. This will only warn about functions listed in :ref:`ASYNC114 ` or known from Trio. If you're starting a function that does not define `task_status`, then neither will trigger. _`ASYNC114` : startable-not-in-config 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. _`ASYNC115` : async-zero-sleep Replace :func:`trio.sleep(0) `/:func:`anyio.sleep(0) ` with the more suggestive :func:`trio.lowlevel.checkpoint`/:func:`anyio.lowlevel.checkpoint`. _`ASYNC116` : long-sleep-not-forever :func:`trio.sleep`/:func:`anyio.sleep` with >24 hour interval should usually be :func:`trio.sleep_forever`/:func:`anyio.sleep_forever`. _`ASYNC118` : cancelled-class-saved Don't assign the value of :func:`anyio.get_cancelled_exc_class` to a variable, since that breaks linter checks and multi-backend programs. _`ASYNC119` : yield-in-cm-in-async-gen ``yield`` in context manager in async generator is unsafe, the cleanup may be delayed until ``await`` is no longer allowed. We strongly encourage you to read :pep:`533` and use `async with aclosing(...) `_. :func:`trio.as_safe_channel` has been designed to be a drop-in replacement to transform any unsafe async generator into a context manager that uses :ref:`streams ` and safely runs the generator in a background task. _`ASYNC120` : await-in-except Dangerous :ref:`checkpoint` inside an ``except`` block. If this checkpoint is cancelled, the current active exception will be replaced by the ``Cancelled`` exception, and cannot be reraised later. This will not trigger when :ref:`ASYNC102 ` does, and if you don't care about losing non-cancelled exceptions you could disable this rule. This is currently not able to detect asyncio shields. To handle this correctly, use a shielded cancel scope with a timeout:: try: await process() except Exception: with trio.fail_after(seconds, shield=True): await cleanup() raise The shield prevents cancellation from replacing the current exception, while the timeout ensures cleanup can't block indefinitely. Use :func:`trio.move_on_after` if you want to suppress timeout errors rather than raise them. _`ASYNC121`: control-flow-in-taskgroup `return`, `continue`, and `break` inside a :ref:`taskgroup_nursery` can lead to counterintuitive behaviour. Refactor the code to instead cancel the :ref:`cancel_scope` inside the TaskGroup/Nursery and place the statement outside of the TaskGroup/Nursery block. In asyncio a user might expect the statement to have an immediate effect, but it will wait for all tasks to finish before having an effect. See `Trio issue #1493 `_ for further issues specific to trio/anyio. _`ASYNC122`: delayed-entry-of-relative-cancelscope :func:`trio.move_on_after`, :func:`trio.fail_after`, :func:`anyio.move_on_after` and :func:`anyio.fail_after` behaves unintuitively if initialization and entry are separated, with the timeout starting on initialization. Trio>=0.27 changes this behaviour, so if you don't support older versions you should disable this check. See `Trio issue #2512 `_. _`ASYNC123`: bad-exception-group-flattening Raising one of the exceptions contained in an exception group will mutate it, replacing the original ``.__context__`` with the group, and erasing the ``.__traceback__``. Dropping this information makes diagnosing errors much more difficult. We recommend ``raise SomeNewError(...) from group`` if possible; or consider using `copy.copy` to shallow-copy the exception before re-raising (for copyable types), or re-raising the error from outside the `except` block. _`ASYNC124`: async-function-could-be-sync Triggers when an async function contain none of ``await``, ``async for`` or ``async with``. Calling an async function is slower than calling regular functions, so if possible you might want to convert your function to be synchronous. This currently overlaps with :ref:`ASYNC910 ` and :ref:`ASYNC911 ` which, if enabled, will autofix the function to have :ref:`checkpoint`. This excludes class methods as they often have to be async for other reasons, if you really do want to check those you could manually run :ref:`ASYNC910 ` and/or :ref:`ASYNC911 ` and check the methods they trigger on. _`ASYNC125`: constant-absolute-deadline Passing constant values (other than :data:`math.inf`) to timeouts expecting absolute deadlines is nonsensical. These should always be defined relative to :func:`trio.current_time`/:func:`anyio.current_time`, or you might want to use :func:`trio.fail_after`/`:func:`trio.move_on_after`/:func:`anyio.fail_after`/ :func:`anyio.move_on_after`, or the ``relative_deadline`` parameter to :class:`trio.CancelScope`. _`ASYNC126`: exceptiongroup-subclass-missing-derive A subclass of :class:`ExceptionGroup` or :class:`BaseExceptionGroup` must override :meth:`~BaseExceptionGroup.derive` to return an instance of itself, otherwise :meth:`~BaseExceptionGroup.split` and :meth:`~BaseExceptionGroup.subgroup` - which are used by e.g. :ref:`taskgroup_nursery` implementations - will silently produce plain ``ExceptionGroup`` instances and lose the custom subclass. See `trio#3175 `_ for motivation. _`ASYNC127`: unmaintained-httpx The original ``httpx`` package is no longer maintained, which is a problem for a networking library. Use `httpx2`_ - the continuation of the project, maintained by Pydantic - to keep getting security updates. It uses the same API, so migration is usually just a matter of replacing ``httpx`` with ``httpx2`` in imports. This rule triggers on any import of ``httpx``. Blocking sync calls in async functions ====================================== Our 2xx lint rules warn you to use the async equivalent for slow sync calls which would otherwise block the event loop (and therefore cause performance problems, or even deadlock). .. _httpx2: https://httpx2.pydantic.dev/ .. _httpx.Client: https://www.python-httpx.org/api/#client .. _httpx2.Client: https://httpx2.pydantic.dev/api/#client .. _httpx2.AsyncClient: https://httpx2.pydantic.dev/api/#asyncclient .. _urllib3: https://github.com/urllib3/urllib3 .. _aiofiles: https://pypi.org/project/aiofiles/ .. _anyio: https://github.com/agronholm/anyio _`ASYNC200` : blocking-configured-call User-configured error for blocking sync calls in async functions. Does nothing by default, see :ref:`async200-blocking-calls` for how to configure it. _`ASYNC210` : blocking-http-call Sync HTTP call in async function, use `httpx2.AsyncClient`_. This and the other :ref:`ASYNC21x ` checks look for usage of `urllib3`_, `httpx.Client`_, and `httpx2.Client`_, and recommend using `httpx2.AsyncClient`_ as that's the largest http client supporting anyio/trio. Sync calls through the unmaintained ``httpx`` package are detected the same way as ``httpx2`` ones, but the recommendation is always `httpx2.AsyncClient`_ - see :ref:`ASYNC127 `. _`ASYNC211` : blocking-http-call-pool Likely sync HTTP call in async function, use `httpx2.AsyncClient`_. Looks for `urllib3`_ method calls on pool objects, but only matching on the method signature and not the object. _`ASYNC212` : blocking-http-call-httpx Blocking sync HTTP call on httpx/httpx2 object, use `httpx2.AsyncClient`_. ASYNC220 : blocking-create-subprocess Sync call to :class:`subprocess.Popen` (or equivalent) in async function, use :func:`trio.run_process`/:func:`anyio.run_process`/:ref:`asyncio.create_subprocess_[exec/shell] ` in a :ref:`taskgroup_nursery`. ASYNC221 : blocking-run-process Sync call to :func:`subprocess.run` (or equivalent) in async function, use :func:`trio.run_process`/:func:`anyio.run_process`/:ref:`asyncio.create_subprocess_[exec/shell] `. ASYNC222 : blocking-process-wait Sync call to :func:`os.wait` (or equivalent) in async function, wrap in :func:`trio.to_thread.run_sync`/:func:`anyio.to_thread.run_sync`/:meth:`asyncio.loop.run_in_executor`. ASYNC230 : blocking-open-call Sync call to :func:`open` in async function, use :func:`trio.open_file`/:func:`anyio.open_file`. ``asyncio`` users need to use a library such as `aiofiles`_, or switch to `anyio`_. ASYNC231 : blocking-fdopen-call Sync call to :func:`os.fdopen` in async function, use :func:`trio.wrap_file`/:func:`anyio.wrap_file`. ``asyncio`` users need to use a library such as `aiofiles`_, or switch to `anyio`_. ASYNC232 : blocking-file-call Blocking sync call on file object, wrap the file object in :func:`trio.wrap_file`/:func:`anyio.wrap_file` to get an async file object. ASYNC240 : blocking-path-usage Avoid using :mod:`os.path` in async functions, prefer using :class:`trio.Path`/:class:`anyio.Path` objects. ``asyncio`` users should consider `aiopath `__ or `anyio`_. ASYNC250 : blocking-input Builtin :func:`input` should not be called from async function. Wrap in :func:`trio.to_thread.run_sync`/:func:`anyio.to_thread.run_sync` or :meth:`asyncio.loop.run_in_executor`. ASYNC251 : blocking-sleep :func:`time.sleep` should not be called from async function. Use :func:`trio.sleep`/:func:`anyio.sleep`/:func:`asyncio.sleep`. Asyncio-specific rules ====================== Asyncio *encourages* structured concurrency, with :obj:`asyncio.TaskGroup`, but does not *require* it. We therefore provide some additional lint rules for common problems - although we'd also recommend a gradual migration to AnyIO, which is much less error-prone. _`ASYNC300` : create-task-no-reference Calling :func:`asyncio.create_task` without saving the result. A task that isn't referenced elsewhere may get garbage collected at any time, even before it's done. Note that this rule won't check whether the variable the result is saved in is susceptible to being garbage-collected itself. See the asyncio documentation for best practices. You might consider instead using a :ref:`TaskGroup ` and calling :meth:`asyncio.TaskGroup.create_task` to avoid this problem, and gain the advantages of structured concurrency with e.g. better cancellation semantics. ExceptionGroup rules ==================== _`ASYNC400` : except-star-invalid-attribute When converting a codebase to use `except* ` it's easy to miss that the caught exception(s) are wrapped in a group, so accessing attributes on the caught exception must now check the contained exceptions. This checks for any attribute access on a caught ``except*`` that's not a known valid attribute on `ExceptionGroup`. This can be safely disabled on a type-checked or coverage-covered code base. Optional rules disabled by default ================================== Our 9xx rules check for semantics issues, like 1xx rules, but are disabled by default due to the higher volume of warnings. We encourage you to enable them - without guaranteed :ref:`checkpoint`\ s timeouts and cancellation can be arbitrarily delayed, and async generators are prone to the problems described in :pep:`789` and :pep:`533`. _`ASYNC900` : unsafe-async-generator Async generator without :func:`@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 :pep:`789` for control-flow problems, :pep:`533` for delayed cleanup problems. Further decorators can be registered with the ``--transform-async-generator-decorators`` config option, e.g. `@trio_util.trio_async_generator `_. _`ASYNC910` : async-function-no-checkpoint Exit or ``return`` from async function with no guaranteed :ref:`checkpoint` or exception since function definition. You might want to enable this on a trio/anyio codebase to make it easier to reason about checkpoints, and make the logic of ASYNC911 correct. _`ASYNC911` : async-generator-no-checkpoint Exit, ``yield`` or ``return`` from async iterable with no guaranteed :ref:`checkpoint` since possible function entry (``yield`` or function definition). _`ASYNC912` : cancel-scope-no-guaranteed-checkpoint A timeout/cancelscope has :ref:`cancel points `, but they're not guaranteed to run. Similar to `ASYNC100`_, but it does not warn on trivial cases where there is no cancel point at all. It instead shares logic with `ASYNC910`_ and `ASYNC911`_ for parsing conditionals and branches. _`ASYNC913` : indefinite-loop-no-guaranteed-checkpoint An indefinite loop (e.g. ``while True``) has no guaranteed :ref:`checkpoint `. This could potentially cause a deadlock. This will also error if there's no guaranteed :ref:`cancel point `, where even though it won't deadlock the loop might become an uncancelable dry-run loop. .. _autofix-support: Autofix support =============== The following rules support :ref:`autofixing `. - :ref:`ASYNC100 ` - :ref:`ASYNC910 ` - :ref:`ASYNC911 ` - :ref:`ASYNC913 ` Removed rules ================ - **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.