Upgrading anyio 3 to 4
Breaking3.7.1 → 4.16.0Breaking10Security3Platform6Deprecated2New57Changed39Fixed157Other4
SecurityAnyIO run_process/open_process ignores extra_groups and can retain parent supplementary groupsGHSA-3w57-8xmc-8v26 · CVE-2026-63349 ↗
BreakingUpgrade verdict
9 breaking changes and 6 platform requirement changes and 3 security fixes.
In plain terms
- Dropped sniffio as a direct dependency and added the get_available_backends() function (\).
- Renamed the BrokenWorkerIntepreter exception to BrokenWorkerInterpreter.
- The option for letting a thread keep running after cancellation has a new name; the old name still works but is deprecated.
- Tasks can no longer be started from old-style coroutine functions.
Must act Top 5 of 20 that need code or config changes
Python requirement changed in 4.13.0: >=3.7 → >=3.10.
What you must change
Breaking9
Dropped sniffio as a direct dependency and added the get_available_backends() function (\).
sniffioget_available_backends()Renamed the BrokenWorkerIntepreter exception to BrokenWorkerInterpreter.
BrokenWorkerIntepreterBrokenWorkerInterpreterRenames the cancellable argument in anyio.to_thread.run_sync() to abandon_on_cancel and deprecates the old parameter name.
anyio.to_thread.run_sync()cancellableabandon_on_cancelDrops support for spawning tasks from old-style coroutine functions decorated with @asyncio.coroutine.
@asyncio.coroutineRenames the asyncio backend policy option to loop_factory to accommodate asyncio.Runner.
policyloop_factoryasyncio.RunnerChanges type annotations of TaskGroup.start_soon(), TaskGroup.start() and anyio.from_thread.run() to only accept callables returning coroutine-like objects.
TaskGroup.start_soon()TaskGroup.start()anyio.from_thread.run()Changes cancellation semantics: exceptions from task groups are nested in ExceptionGroup, and several cancellation behaviors change.
ExceptionGroupBaseExceptionGroupTaskGroupChanges anyio.Path.relative_to() and anyio.Path.is_relative_to() to only accept one argument.
anyio.Path.relative_to()anyio.Path.is_relative_to()Changes the pytest plugin to run all tests and fixtures in the same task, allowing fixtures to set context variables.
Security3
AnyIO run_process/open_process ignores extra_groups and can retain parent supplementary groups
AnyIO: TLSStream IDNA 2003 host name encoding enables potential TLS certificate spoofing
AnyIO process-pool workers can block indefinitely on undrained stderr
Platform6
Adds support for Python 3.15.
Drops support for Python 3.9.
Added the ability to set the token count of a CapacityLimiter to zero (\; requires Python 3.10 or later when using Trio).
CapacityLimiterDrops support for Python 3.8 because issue #698 cannot be resolved without cancel message support.
Drops support for Python 3.7.
Adds support for Python 3.12.
Deprecations to plan for
Deprecated2
Deprecates the item_type argument of create_memory_object_stream in favor of generic subscription.
item_typecreate_memory_object_streamcreate_memory_object_stream[T_Item]()Re-adds the item_type argument to create_memory_object_stream(), which now raises a deprecation warning and has no effect on static types.
create_memory_object_stream()item_typeAPI changes
Generated by DepDelta from published Python source. Not mixed with prose notes.
- removed:
export ExceptionGroup - removed:
export create_blocking_portal - removed:
export create_capacity_limiter - removed:
export create_condition - removed:
export create_event - removed:
export create_lock - removed:
export create_semaphore - removed:
export current_default_worker_thread_limiter - removed:
export maybe_async - removed:
export maybe_async_cm - removed:
export open_cancel_scope - removed:
export run_async_from_thread - removed:
export run_sync_from_thread - removed:
export run_sync_in_worker_thread - removed:
export start_blocking_portal - added:
export AsyncContextManagerMixin - added:
export BrokenWorkerInterpreter - added:
export ConnectionFailed - added:
export ContextManagerMixin - added:
export Future - added:
export FutureAlreadyFinished - added:
export FutureCancelled - added:
export FutureFailed - added:
export FutureNotFinished - added:
export NamedTemporaryFile - added:
export NoEventLoopError - added:
export ResourceGuard - added:
export RunFinishedError - added:
export SpooledTemporaryFile - added:
export TCPConnectable - added:
export TaskCancelled - added:
export TaskFailed - added:
export TaskHandle - added:
export TaskNotFinished - added:
export TemporaryDirectory - added:
export TemporaryFile - added:
export UNIXConnectable - added:
export abc - added:
export amap - added:
export as_completed - added:
export as_connectable - added:
export create_connected_unix_datagram_socket - added:
export create_unix_datagram_socket - added:
export fail_at - added:
export fix_package_names - added:
export from_thread - added:
export functools - added:
export gather - added:
export get_available_backends - added:
export gettempdir - added:
export gettempdirb - added:
export install_lazy_importer - added:
export itertools - added:
export lowlevel - added:
export mkdtemp - added:
export mkstemp - added:
export move_on_at - added:
export notify_closing - added:
export set_deprecated_aliases - added:
export to_interpreter - added:
export to_process - added:
export to_thread - added:
export wait_readable - added:
export wait_writable
- removed:
export BlockingPortal - removed:
export CancelScope - removed:
export CapacityLimiter - removed:
export Condition - removed:
export Event - removed:
export Lock - removed:
export Semaphore - added:
export AnyByteStreamConnectable - added:
export AsyncBackend - added:
export ByteStreamConnectable - added:
export ConnectedUNIXDatagramSocket - added:
export ObjectStreamConnectable - added:
export UNIXDatagramPacketType - added:
export UNIXDatagramSocket - added:
export fix_package_names - added:
export install_lazy_importer - added:
export set_deprecated_aliases
- removed:
function create_blocking_portal() -> BlockingPortal - removed:
function run_async_from_thread(func: Callable[..., Awaitable[T_Retval]], *args: object) -> T_Retval - removed:
function run_sync_from_thread(func: Callable[..., T_Retval], *args: object) -> T_Retval - removed:
method BlockingPortal.spawn_task(func: Callable[..., Awaitable[T_Retval]], *args: object, name: object=…) -> Future[T_Retval] | (func: Callable[..., T_Retval], *args: object, name: object=…) -> Future[T_Retval] - removed:
variable T_Retval - removed:
variable T_co - changed:
function run(func: Callable[..., Awaitable[T_Retval]], *args: object) -> T_Retval → function run(func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], *args: Unpack[PosArgsT], token: EventLoopToken | None=…) -> T_co - changed:
function run_sync(func: Callable[..., T_Retval], *args: object) -> T_Retval → function run_sync(func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT], token: EventLoopToken | None=…) -> T_Retval - changed:
function start_blocking_portal(backend: str=…, backend_options: dict[str, Any] | None=…) -> Generator[BlockingPortal, Any, None] → function start_blocking_portal(backend: str=…, backend_options: dict[str, Any] | None=…, *, name: str | None=…) -> Generator[BlockingPortal, Any, None] - changed:
method BlockingPortal.call(func: Callable[..., Awaitable[T_Retval]], *args: object) -> T_Retval | (func: Callable[..., T_Retval], *args: object) -> T_Retval → method BlockingPortal.call(func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], *args: Unpack[PosArgsT]) -> T_Retval | (func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT]) -> T_Retval - changed:
method BlockingPortal.start_task(func: Callable[..., Awaitable[Any]], *args: object, name: object=…) -> tuple[Future[Any], Any] → method BlockingPortal.start_task(func: Callable[..., Awaitable[T_Retval]], *args: object, name: object=…) -> tuple[Future[T_Retval], Any] - changed:
method BlockingPortal.start_task_soon(func: Callable[..., Awaitable[T_Retval]], *args: object, name: object=…) -> Future[T_Retval] | (func: Callable[..., T_Retval], *args: object, name: object=…) -> Future[T_Retval] → method BlockingPortal.start_task_soon(func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], *args: Unpack[PosArgsT], name: object=…) -> Future[T_Retval] | (func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT], name: object=…) -> Future[T_Retval] - changed:
method BlockingPortal.wrap_async_context_manager(cm: AsyncContextManager[T_co]) -> ContextManager[T_co] → method BlockingPortal.wrap_async_context_manager(cm: AbstractAsyncContextManager[T_co]) -> AbstractContextManager[T_co] - added:
async method BlockingPortal.call_async(func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], *args: Unpack[PosArgsT], abandon_on_cancel: bool=…) -> T_Retval | (func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT], abandon_on_cancel: bool=…) -> T_Retval - added:
class BlockingPortalProvider - added:
field BlockingPortalProvider.backend: str - added:
field BlockingPortalProvider.backend_options: dict[str, Any] | None - added:
function check_cancelled() -> None
- added:
async function reduce(function: Callable[[T, S], Awaitable[T]], iterable: Iterable[S] | AsyncIterable[S], /, initial: T) -> T | (function: Callable[[T, T], Awaitable[T]], iterable: Iterable[T] | AsyncIterable[T], /) -> T - added:
class AsyncCacheInfo(NamedTuple) - added:
class AsyncCacheParameters(TypedDict) - added:
class AsyncLRUCacheWrapper(Generic[P, T]) - added:
constructor AsyncLRUCacheWrapper(func: Callable[P, Awaitable[T]], maxsize: int | None, typed: bool, always_checkpoint: bool, ttl: int | None) - added:
field AsyncCacheInfo.currsize: int - added:
field AsyncCacheInfo.hits: int - added:
field AsyncCacheInfo.maxsize: int | None - added:
field AsyncCacheInfo.misses: int - added:
field AsyncCacheInfo.ttl: int | None - added:
field AsyncCacheParameters.always_checkpoint: bool - added:
field AsyncCacheParameters.maxsize: int | None - added:
field AsyncCacheParameters.ttl: int | None - added:
field AsyncCacheParameters.typed: bool - added:
function cache(func: Callable[P, Coroutine[Any, Any, T]], /) -> AsyncLRUCacheWrapper[P, T] | (func: Callable[..., T], /) -> functools._lru_cache_wrapper[T] - added:
function lru_cache(*, maxsize: int | None=…, typed: bool=…, always_checkpoint: bool=…, ttl: int | None=…) -> _LRUCacheWrapper | (func: Callable[P, Coroutine[Any, Any, T]], /) -> AsyncLRUCacheWrapper[P, T] | (func: Callable[..., T], /) -> functools._lru_cache_wrapper[T] - added:
method AsyncLRUCacheWrapper.cache_clear() -> None - added:
method AsyncLRUCacheWrapper.cache_info() -> AsyncCacheInfo - added:
method AsyncLRUCacheWrapper.cache_parameters() -> AsyncCacheParameters
- added:
async function accumulate(iterable: Iterable[T] | AsyncIterable[T], function: Callable[[T, T], Awaitable[T]]=…, *, initial: T | None=…) -> AsyncGenerator[T, None] - added:
async function batched(iterable: Iterable[T] | AsyncIterable[T], n: int, *, strict: bool=…) -> AsyncGenerator[tuple[T, ...], None] - added:
async function combinations(iterable: Iterable[T] | AsyncIterable[T], r: int) -> AsyncGenerator[tuple[T, ...], None] - added:
async function combinations_with_replacement(iterable: Iterable[T] | AsyncIterable[T], r: int) -> AsyncGenerator[tuple[T, ...], None] - added:
async function compress(data: Iterable[T] | AsyncIterable[T], selectors: Iterable[object] | AsyncIterable[object]) -> AsyncGenerator[T, None] - added:
async function count(start: int=…, step: int=…) -> AsyncGenerator[int, None] - added:
async function cycle(iterable: Iterable[T] | AsyncIterable[T]) -> AsyncGenerator[T, None] - added:
async function dropwhile(predicate: Callable[[T], Awaitable[object]], iterable: Iterable[T] | AsyncIterable[T]) -> AsyncGenerator[T, None] - added:
async function filterfalse(predicate: Callable[[T], Awaitable[object]], iterable: Iterable[T] | AsyncIterable[T]) -> AsyncGenerator[T, None] - added:
async function pairwise(iterable: Iterable[T] | AsyncIterable[T]) -> AsyncGenerator[tuple[T, T], None] - added:
async function permutations(iterable: Iterable[T] | AsyncIterable[T], r: int | None=…) -> AsyncGenerator[tuple[T, ...], None] - added:
async function product(*iterables: Iterable[T] | AsyncIterable[T], repeat: int=…) -> AsyncGenerator[tuple[T, ...], None] - added:
async function repeat(element: T, times: int | None=…) -> AsyncGenerator[T, None] - added:
async function starmap(function: Callable[..., Awaitable[R]], iterable: Iterable[Iterable[object] | AsyncIterable[object]] | AsyncIterable[Iterable[object] | AsyncIterable[object]]) -> AsyncGenerator[R, None] - added:
async function takewhile(predicate: Callable[[T], Awaitable[object]], iterable: Iterable[T] | AsyncIterable[T]) -> AsyncGenerator[T, None] - added:
async function zip_longest(*iterables: Iterable[object] | AsyncIterable[object], fillvalue: object=…) -> AsyncGenerator[tuple[object, ...], None] - added:
async method Chain.from_iterable(iterables: Iterable[Iterable[T] | AsyncIterable[T]] | AsyncIterable[Iterable[T] | AsyncIterable[T]]) -> AsyncGenerator[T, None] - added:
class Chain - added:
function groupby(iterable: Iterable[T] | AsyncIterable[T]) -> AsyncGenerator[tuple[T, list[T]], None] | (iterable: Iterable[T] | AsyncIterable[T], key: Callable[[T], Awaitable[R]]) -> AsyncGenerator[tuple[R, list[T]], None] - added:
function islice(iterable: Iterable[T] | AsyncIterable[T], stop: int | None, /) -> AsyncGenerator[T, None] | (iterable: Iterable[T] | AsyncIterable[T], start: int | None, stop: int | None, step: int | None=…, /) -> AsyncGenerator[T, None] - added:
function tee(iterable: Iterable[T] | AsyncIterable[T], n: int=…) -> tuple[AsyncIterator[T], ...]
- removed:
variable D - removed:
variable T - changed:
function current_token() -> object → function current_token() -> EventLoopToken - added:
class EventLoopToken - added:
field EventLoopToken.backend_class: type[AsyncBackend] - added:
field EventLoopToken.native_token: object
- changed:
function get_runner(backend_name: str, backend_options: dict[str, Any]) -> Generator[TestRunner, object, None] → function get_runner(backend_name: str, backend_options: dict[str, Any]) -> Iterator[TestRunner] - changed:
function pytest_configure(config: Any) -> None → function pytest_configure(config: pytest.Config) -> None - changed:
function pytest_fixture_setup(fixturedef: Any, request: Any) -> None → function pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any] - changed:
function pytest_pycollect_makeitem(collector: Any, name: Any, obj: Any) -> None → function pytest_pycollect_makeitem(collector: pytest.Module | pytest.Class, name: str, obj: object) -> None - added:
class FreePortFactory - added:
constructor FreePortFactory(kind: socket.SocketKind) -> None - added:
function free_tcp_port(free_tcp_port_factory: Callable[[], int]) -> int - added:
function free_tcp_port_factory() -> FreePortFactory - added:
function free_udp_port(free_udp_port_factory: Callable[[], int]) -> int - added:
function free_udp_port_factory() -> FreePortFactory - added:
function pytest_addoption(parser: pytest.Parser) -> None - added:
function pytest_collection_finish(session: pytest.Session) -> None - added:
property FreePortFactory.kind -> socket.SocketKind
- added:
async method BufferedByteStream.send(item: bytes) -> None - added:
async method BufferedByteStream.send_eof() -> None - added:
async method BufferedConnectable.connect() -> BufferedByteStream - added:
class BufferedByteStream(BufferedByteReceiveStream, ByteStream) - added:
class BufferedConnectable(ByteStreamConnectable) - added:
constructor BufferedByteStream(stream: AnyByteStream) - added:
constructor BufferedConnectable(connectable: AnyByteStreamConnectable) - added:
method BufferedByteReceiveStream.feed_data(data: Iterable[SupportsIndex], /) -> None
- changed:
field FileStreamAttribute.file: BinaryIO → field FileStreamAttribute.file: IO[bytes]
- removed:
class MemoryObjectStreamState(Generic[T_Item]) - removed:
field MemoryObjectStreamState.buffer: deque[T_Item] - removed:
field MemoryObjectStreamState.max_buffer_size: float - removed:
field MemoryObjectStreamState.open_receive_channels: int - removed:
field MemoryObjectStreamState.open_send_channels: int - removed:
field MemoryObjectStreamState.waiting_receivers: OrderedDict[Event, list[T_Item]] - removed:
field MemoryObjectStreamState.waiting_senders: OrderedDict[Event, T_Item] - removed:
method MemoryObjectStreamState.statistics() -> MemoryObjectStreamStatistics - removed:
variable T_Item - removed:
variable T_co - removed:
variable T_contra - changed:
class MemoryObjectReceiveStream(Generic[T_co], ObjectReceiveStream[T_co]) → class MemoryObjectReceiveStream(ObjectReceiveStream[T_co], Generic[T_co]) - changed:
class MemoryObjectSendStream(Generic[T_contra], ObjectSendStream[T_contra]) → class MemoryObjectSendStream(ObjectSendStream[T_contra], Generic[T_contra]) - changed:
method MemoryObjectSendStream.send_nowait(item: T_contra) -> DeprecatedAwaitable → method MemoryObjectSendStream.send_nowait(item: T_contra) -> None
- removed:
variable T_Item - removed:
variable T_Stream - changed:
class MultiListener(Generic[T_Stream], Listener[T_Stream]) → class MultiListener(Listener[T_Stream], Generic[T_Stream]) - changed:
class StapledObjectStream(Generic[T_Item], ObjectStream[T_Item]) → class StapledObjectStream(ObjectStream[T_Item], Generic[T_Item]) - added:
method StapledObjectStream.send_nowait(item: T_Item) -> None
- added:
async method TextConnectable.connect() -> TextStream - added:
class TextConnectable(ObjectStreamConnectable[str]) - added:
constructor TextConnectable(connectable: AnyByteStreamConnectable)
- removed:
variable T_Retval - changed:
field TLSAttribute.peer_certificate: dict[str, str | _PCTRTTT | _PCTRTT] | None → field TLSAttribute.peer_certificate: None | dict[str, str | _PCTRTTT | _PCTRTT] - added:
async method TLSConnectable.connect() -> TLSStream - added:
class TLSConnectable(ByteStreamConnectable) - added:
constructor TLSConnectable(connectable: AnyByteStreamConnectable, *, hostname: str | None=…, ssl_context: ssl.SSLContext | None=…, standard_compatible: bool=…) -> None
- added:
async function run_sync(func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT], limiter: CapacityLimiter | None=…) -> T_Retval - added:
function current_default_interpreter_limiter() -> CapacityLimiter
- removed:
variable T_Retval - removed:
variable WORKER_MAX_IDLE_TIME - changed:
async function run_sync(func: Callable[..., T_Retval], *args: object, cancellable: bool=…, limiter: CapacityLimiter | None=…) -> T_Retval → async function run_sync(func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT], cancellable: bool=…, limiter: CapacityLimiter | None=…) -> T_Retval
- removed:
async function run_sync_in_worker_thread(func: Callable[..., T_Retval], *args: object, cancellable: bool=…, limiter: CapacityLimiter | None=…) -> T_Retval - removed:
function current_default_worker_thread_limiter() -> CapacityLimiter - removed:
variable T_Retval - changed:
async function run_sync(func: Callable[..., T_Retval], *args: object, cancellable: bool=…, limiter: CapacityLimiter | None=…) -> T_Retval → async function run_sync(func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT], abandon_on_cancel: bool=…, cancellable: bool | None=…, limiter: CapacityLimiter | None=…) -> T_Retval