Repository navigation
Releases
4.16.0
New features
- Added
BlockingPortal.call_async()for asynchronously calling into an event loop with optional abandonment on cancellation (#1342; PR by @davidbrochart) - Added SOCK_SEQPACKET support to
connect_unix()andcreate_unix_listener()(#1349; PR by @theunkn0wn1)
Other changes
- Changed UDP sockets on the asyncio backend to make
send()wait until the datagram has been passed to the operating system (#1294; PR by @graingert)
Bug fixes
- Fixed documentation implying that
use_uvloop=Truefalls back to the standard event loop when uvloop (winloop on Windows) is not installed, and showed how to skip the corresponding pytest backend variant when the dependency is missing (#645; PR by @GruffElixir) - Fixed
get_current_task()on the asyncio backend raising an opaqueTypeError: cannot create weak reference to 'NoneType' objectinstead of a clear error when called from a callback scheduled vialoop.call_soon_threadsafe()(e.g. from withinfrom_thread.run_sync()), whereasyncio.current_task()is legitimatelyNone(#773; PR by @AmirF194) - Fixed
CancelScopeon the asyncio backend swallowing a nativeTask.cancel()that was delivered in the same event loop iteration as the scope's own cancellation (such as amove_on_after()deadline expiring), which left the host task with a pending cancellation request that never raisedCancelledError(#1214) - Fixed UDP socket closing hanging if a datagram send was still in flight (#1237; PR by @graingert)
- Fixed concurrent
aclose_forcefully()calls on asyncio socket streams returning before the underlying socket was closed (#1273; PR by @graingert) - Fixed
aclose()on sockets not checkpointing, and thus failing to raise a cancellation exception when called inside a cancelled scope (affected UNIX socket streams and UNIX datagram sockets on asyncio, and all sockets on Trio) (#1288; PR by @graingert) - Fixed
aclose()on asyncio TCP socket listeners not checkpointing, and leaking the listening socket if cancelled while another task was waiting inaccept()(#1289; PR by @graingert) - Fixed
accept()on asyncio UNIX socket listeners hanging forever, and leaving the listening socket registered with the event loop, when the listener was closed by another task (#1289; PR by @graingert) - Fixed
send()on asyncio TCP and UDP sockets returning normally, despite the data being discarded, when the socket was closed by another task (or, for TCP, the connection was lost) whilesend()was waiting for the operating system to accept the data (#1289; PR by @graingert) - Fixed
SocketStream.send()on the asyncio backend handing its data to a paused transport after a previoussend()was cancelled (#1299; PR by @graingert) - Fixed
sleep()treating a negative delay (including-inf) inconsistently across backends (asyncio returned immediately; Trio raisedValueError) by raisingValueErroron all backends (#1306; PR by @BetterAndBetterII) - Fixed
create_tcp_listener()occasionally failing with a message likeCould not create 2 listeners with a consistent portwhen an ephemeral port is requested and IPv6 is enabled and the dual-stack path is not available or a specific local host name was given (#1313) - Fixed
AsyncFilenot shielding against cancellation while closing (#1314) - Fixed
TemporaryDirectory.cleanup()reacting to cancellation prematurely when the host task was already cancelled, causing the actual cleanup operation to be skipped (#1316; PR by @Yasser-Ameur) - Fixed a NaN deadline being accepted on asyncio while Trio raised
ValueError, which silently disabled the timeout (fail_after(math.nan)returned instead of raisingTimeoutError);CancelScopenow rejects it on both backends (#1317; PR by @dylanpulver) - Fixed
FileReadStreamandFileWriteStreamleaving files open when cancelled during context manager exit, including unflushed writes (#1318; PR by @Kuang-xianxin) - Fixed
Condition.notify()andCondition.notify_all()failing when the underlying lock was acquired directly rather than through the condition (#1319; PR by @GruffElixir) - Fixed importing AnyIO with loaders that do not provide
__file__, such as PyOxidizer, raisingTypeErrorinstead of falling back to eager imports (#1322; PR by @skulitom) - Fixed text receive streams silently discarding incomplete characters at end-of-file instead of applying the configured decoding error handler (#1339; PR by @Aurelucien)
- Fixed
Semaphore(fast_acquire=True)losing its fast-acquire behavior when instantiated outside a running event loop (#1348; PR by @feiiiiii5) - Fixed
BlockingPortalProviderpermanently failing with anAttributeErroron every subsequent use if the first attempt to start its blocking portal failed (#1350; PR by @Yasser-Ameur) - Fixed
create_udp_socket()silently ignoring thelocal_portargument whenlocal_hostwas omitted, making the socket bind to a random ephemeral port instead of the requested one (#1351; PR by @januththedev) - Fixed worker threads on the asyncio backend never returning to the idle pool (and thus never being reused or pruned) when a
to_thread.run_sync()call was cancelled after it had been queued for the worker but before the worker thread had picked it up (#1352; PR by @Zac-HD) - Fixed pytest plugin running Hypothesis async tests only on the first parametrized backend instead of each backend (#1353; PR by @DYNOSuprovo)
- Fixed
TypeErrorwhen subclassing an existingTypedAttributeSetsubclass, and this subclass contains type annotations (#1358; PR by @pasmud) - Fixed
Lock.locked()(andCondition.locked()) raisingNoEventLoopErrorwhen the lock was created outside a running event loop (#1362; PR by @00200200) - Fixed
connect_tcp()leaking a connected socket when an enclosing cancel scope was cancelled before the connection attempt task group exited (#1364; PR by @charan-rathore) - Fixed
create_connected_udp_socket()silently ignoringlocal_portwhenlocal_hostwas not given: the local address was left unset, so the OS picked an ephemeral port instead of the requested one (#1365; PR by @feiiiiii5) - Fixed
BlockingPortalProvidernot passing the exception raised in itswithblock on to thestart_blocking_portal()context manager it wraps (#1366; PR by @feiiiiii5) - Fixed
anyio.itertools.islice()not consuming the firststartitems whenstop <= start(#1372; PR by @Rodrigo-Palma) - Fixed
BufferedByteReceiveStream.receive_exactly()andreceive_until()returning buffered data after explicit closure instead of raisingClosedResourceError(#1378; PR by @kokotatan) - Fixed a TLS stream sending records the peer could no longer decrypt after a cancelled
send()lost an already-encrypted record; the stream now raisesBrokenResourceErroron further use instead (#1385; PR by @Kayvan-Zahiri) - Fixed
close()on memory object streams not waking up tasks blocked insend()orreceive()on the same stream (#1391) - Fixed
anyio.itertools.product()acquiring and consuming unused input iterables whenrepeat=0(#1394; PR by @sricharanreddycheruku) - Fixed
SpooledTemporaryFilerolling over to disk on writes whenmax_sizeis zero (the default) (#1397; PR by @fhgffy) - Fixed Trio UDP sockets remaining open when initialization failed or was cancelled (#1400; PR by @adenzhou1350)
- Fixed SpooledTemporaryFile.rollover() moving the current file position to the end of the file (#1401; PR by @fhgffy)
- Fixed cancellation during temporary directory creation leaving the directory behind while its context manager is retained (#1403; PR by @adenzhou1350)
- Fixed cancellation during
SpooledTemporaryFile.rollover()losing buffered contents (#1404; PR by @adenzhou1350) - Fixed
TextSendStream.send()re-emitting the byte order mark on every item with BOM-emitting encodings (utf-16,utf-32,...
4.15.1
- Implemented a compatibility fix for supporting direct access of
anyio.*submodules from the main package even when those submodules were not directly imported first (#1311)
4.15.0
-
Added support for the newer keyword-only arguments on
anyio.Pathmethods to match the standard librarypathlib.Path:follow_symlinksonexists()(Python 3.12+)follow_symlinksonis_dir()(Python 3.13+)follow_symlinksonis_file()(Python 3.13+)follow_symlinksonowner()(Python 3.13+)follow_symlinksongroup()(Python 3.13+)newlineonread_text()(Python 3.13+)
(#1286, #1293; PR by @jaideeppyne)
-
Added
amap,gather, andas_completedutility functions to simplify common patterns (#1173; PR by @Graeme22) -
Added
--anyio-modecommand-line option as an alternative to theanyio_modeini setting, and fix the pytest plugin's auto mode detection to recognize the mode when set via either mechanism(e.g:pytest_asyncio). (#1242; PR by @EmmanuelNiyonshuti) -
Added the
anyio.Futuresynchronization primitive which behaves similar toasyncio.Future, allowing tasks to wait for a value (or exception) from another task (#1146; PR by @Vizonex) -
Added guidance for managing multiple memory object stream producers and consumers with cloned streams (#330; PR by @nightcityblade)
-
Added
StapledObjectStream.send_nowait()that delegates to the underlyingObjectSendStream, if it implements it (#1241; PR by @davidbrochart) -
Added the
move_on_at()andfail_at()functions to complementmove_on_after()andfail_after() -
Changed the default name for a task spawned with
TaskGroup.create_task(func())to match the default task name for the analogous task spawned withTaskGroup.start_soon(func)orTaskGroup.start(func)in more situations. Previously, the default name of aTaskGroup.create_tasktask never included the module name. (The default name for a task spawned withTaskGroup.start_soonorTaskGroup.starttypically includes the module name.) (#1234; PR by @gschaffner) -
Changed the
anyioandanyio.abcmodules to lazily (much like810) import the necessary submodules. This is done by parsing the AST of the module and building a lookup table from theif TYPE_CHECKING:block. A fallback mode has been provided for installations where the source code is unavailable (e.g. PyInstaller). (#1169) -
Fixed free-threading compatibility issues arising from the fact that on Python 3.14 free-threading builds, newly created threads inherit the current context by default, causing AnyIO to behave erroneously in relation to
start_blocking_portal()andanyio.to_thread.run_sync()(#1224; PR by @EmmanuelNiyonshuti) -
Fixed
SpooledTemporaryFile.readinto()andreadinto1()reading twice before rollover, so the destination buffer was overwritten by the second read and the file position advanced twice, silently losing data (#1215; PR by @c-tonneslan) -
Added a
reasonparameter tofail_after(and the newfail_at) allowing for added exception context when raisingTimeoutError(#1227; PR by @Graeme22) -
Fixed the default
TaskHandle.namemissing part of the task name for tasks started withTaskGroup.starton Trio (#1231; PR by @gschaffner) -
Fixed
anyio.runleaking, or at least, delaying collection of loop and root_task due to the root task being cached in aRunVar. (#1203; PR by @tapetersen) -
Fixed
anyio.Path.with_stem()silently producing a wrong path (e.g.Path(".txt")) instead of raisingValueErrorwhen given an empty stem on a path with a non-empty suffix, unlikepathlib.PurePath.with_stem(#1200; PR by @Sanjays2402) -
Fixed
UNIXSocketStream.aclose()raisingasyncio.InvalidStateErrorwhen a concurrent receive or send operation had just been cancelled on the asyncio backend (#1267; PR by @alloutflo) -
Fixed the pytest plugin importing the deprecated
_pytest.python.CallSpec2alias, which triggersPytestRemovedIn10Warningonpytest>=9.2and crashes pytest at startup whenfilterwarnings = erroris configured (#1271; PR by @matthewfeickert) -
Fixed an asyncio worker thread race that could raise
RuntimeErrorwhen the event loop closed between checking its state and scheduling the worker result (#1265; PR by @hansu650) -
Fixed
CapacityLimiteron the asyncio backend over-granting tokens whentotal_tokenswas raised while the limiter was over-subscribed (#1223; PR by @zelinewang) -
Fixed asyncio task groups leaking unawaited coroutines when a custom task constructor fails; default task creation is unaffected (#1274; PR by @dsfaccini)
-
Fixed inconsistencies between Trio and asyncio when target
TaskGroupis cancelled before a task created with.start()callsTaskStatus.started()- The started task shouldn't get a
CancelledErroruntil the first checkpoint after thestarted()call. - A value passed to
started()should be available on theTaskHandleand correctly passed back to the caller of start even if cancelled. - The CancelledError shouldn't leak out of the
TaskGroup.start()call to the calling task.
(#1197; PR by @tapetersen)
- The started task shouldn't get a
-
Fixed
TemporaryDirectorynot cleaning up when the host task was cancelled while exiting the context manager, as the cleanup now runs in a shielded cancel scope (#1304; PR by @smurfix)
4.14.2
- Changed
ByteReceiveStream.receive()implementations to raise aValueErrorwhenmax_bytesis not a positive integer (#1191) - Fixed
CapacityLimiter.total_tokensrejectingfloat("inf")when the limiter was instantiated outside of an event loop. The adapter setter checked for infinity by identity (value is math.inf), so only the exactmath.infsingleton was accepted, while every backend setter (usingmath.isinf()) accepts any positive infinity (#1189; PR by @greymoth-jp). - Fixed
to_process.run_sync()deadlocking when the worker function writes enough data tosys.stderrto fill the (undrained) pipe buffer. The worker process now redirectssys.stderrtoos.devnullas well, matching the documented behavior - Fixed
TLSStream.wrap()matching an internationalized (unicode) host name against the peer certificate using IDNA 2003 (via the standard library) instead of IDNA 2008, which could cause the host name to be matched against the wrong certificate (#1208) - Fixed
anyio.open_process()(andrun_process()) ignoring theextra_groupsargument, as it mistakenly passed the value of thegroupargument instead (#1209) - Fixed
CapacityLimiter.acquire_nowait()andCapacityLimiter.acquire_nowait_on_behalf_of()raisingtrio.WouldBlockinstead ofanyio.WouldBlockon thetriobackend when there are no tokens available (#1218) - Fixed
CapacityLimiteron the asyncio backend over-granting tokens (borrowed_tokensexceedingtotal_tokensandavailable_tokensgoing negative) when a non-blocking acquire was made in the window between a token being released and the notified waiter resuming. The freed token is now reserved for the woken waiter right away, so the non-blocking acquire correctly raisesWouldBlock(#1170; PR by @gaoflow) - Fixed unnecessary CPU spin when delivering cancellation from
CancelScopeon asyncio under certain conditions, including improper cancel scope nesting (#1111)
4.14.1
- Fixed teardown of higher-scoped async fixtures failing on asyncio with
RuntimeError: Attempted to exit cancel scope in a different task than it was entered inwhen an async test raise an outcome exception (e.g.,pytest.skip(),pytest.xfail(), orpytest.fail()) (#1179; PR by @EmmanuelNiyonshuti) - Fixed
CapacityLimiter.total_tokensrejecting a value of0when the limiter was instantiated outside of an event loop, contradicting the documented behavior of allowing 0 total tokens (#1183; PR by @nyxst4ck)
4.14.0
-
Added support for Python 3.15
-
Added an asynchronous implementation of the
itertoolsmodule (#998; PR by @11kkw) -
Added the
local_portparameter toconnect_tcp()to allow binding to a specific local port before connecting (#1067; PR by @nullwiz) -
Added support for custom capacity limiters in async path and file I/O functions and classes
-
Added the
create_task()task group method for easier asyncio migration (returns aTaskHandle) (#1098) -
Changed
TaskGroup.start_soon()to return aTaskHandle -
Added an option for
TaskGroup.start()to return aTaskHandle(which then contains the start value in thestart_valueproperty) -
Added the
cancel()convenience method toTaskGroupas a shortcut for cancelling the task group's cancel scope -
Improved the error message when a known backend is not installed to suggest the install command (#1115; PR by @EmmanuelNiyonshuti)
-
Improved
anyio.Pathto preserve subclass types by returningSelfin methods that return path objects (#1130; PR by @EmmanuelNiyonshuti) -
Changed the parameter type annotation in
anyio.Path.write_bytes()to accept anyReadableBuffer, thus allowing it to acceptbytearrayandmemoryviewto matchpathlib.Path.write_bytes()(#1135; PR by @SAY-5) -
Changed several type annotations to only accept callables returning coroutine-like objects instead of arbitrary awaitables:
TaskGroup.start_soon()TaskGroup.start()anyio.from_thread.run()
This reverts an earlier change from v3.7.0 which was made in error. (#1153)
-
Changed
anyio.runto support callables returning arbitrary awaitables at runtime on all backends. Previously, this only worked on asyncio (#1171; PR by @gschaffner) -
Changed several classes (and their subclasses) to have
__slots__(with__weakref__):anyio.CancelScopeanyio.CapacityLimiteranyio.Conditionanyio.Eventanyio.Lockanyio.ResourceGuardanyio.Semaphore
-
Fixed cancellation exception escaping a cancel scope when triggered via
check_cancelled()in a worker thread (#1113) -
Fixed
TaskGroupraisingAttributeErrorinstead of a clear error when entered more than once (#1109; PR by @Bahtya) -
Fixed lost type information when passing arguments to
lru_cache(#1104; PR by @Graeme22) -
Fixed test resumption after
KeyboardInterruptin async generator fixtures on the asyncio backend (#1060; PR by @EmmanuelNiyonshuti) -
Fixed import of
__main__into_processworkers when entrypoint script doesn't end in.py, such as when usingconsole_scriptentrypoints. (#1027; PR by @tapetersen) -
Fixed
SocketListener.from_socket()returning a TCP listener forAF_UNIXlistening sockets, causingaccept()to fail withENOTSUP(#1132; PR by @kudato) -
Fixed
UDPSocket.aclose()andConnectedUDPSocket.aclose()on asyncio returning before the underlying socket FD was actually released (#1147; PR by @matias-arrelid) -
Fixed trio backend test runner hanging indefinitely instead of raising an error when dynamically accessing an async fixture via
request.getfixturevalue(#1148; PR by @EmmanuelNiyonshuti) -
Fixed cancelling tasks started through a
BlockingPortalafter the portal has been stopped (#1013; PR by @puneetdixit200) -
Fixed
backend_optionsbeing ignored when running the Trio backend viaanyio.run(); the options are now passed as keyword arguments totrio.run()again, as documented (a regression from AnyIO 3) (#1161; PR by @Zac-HD) -
Fixed asyncio
LockandSemaphoredeadlocks caused by cancelled waiters left queued during release (#1145; PR by @rasmusfaber, @x42005e1f and @agronholm)
4.13.0
- Dropped support for Python 3.9
- Added a
ttlparameter to theanyio.functools.lru_cachewrapper (#1073; PR by @Graeme22) - Widened the type annotations of file I/O streams to accept
IO[bytes]instead of justBinaryIO(#1078) - Fixed
anyio.Pathnot being compatible with Python 3.15 due to the removal ofpathlib.Path.is_reserved()and the addition ofpathlib.Path.__vfspath__()(#1061; PR by @veeceey) - Fixed the
BrokenResourceErrorraised by the asyncioSocketStreamnot having the original exception as its cause (#1055; PR by @veeceey) - Fixed the
TypeErrorraised when using "func" as a parameter name inpytest.mark.parametrizewhen using the pytest plugin (#1068; PR by @JohnnyDeuss) - Fixed the pytest plugin not running tests that had the
anyiomarker added programmatically viapytest_collection_modifyitems(#422; PR by @chbndrhnns) - Fixed cancellation exceptions leaking from a
CancelScopeon asyncio when they are contained in an exception group alongside non-cancellation exceptions (#1091; PR by @gschaffner) - Fixed
Condition.wait()not passing on a notification when the task is cancelled but already received a notification - Fixed inverted condition in the process pool shutdown phase which would cause still-running pooled processes not to be terminated (#1074; PR by @bysiber)
4.12.1
4.12.0
- Added support for asyncio's task call graphs on Python 3.14 and later when using AnyIO's task groups (#1025)
- Added an asynchronous implementation of the
functoolsmodule (#1001) - Added support for
uvloop=Trueon Windows via the winloop implementation (#960; PR by @Vizonex) - Added support for use as a context manager to
anyio.lowlevel.RunVar(#1003) - Added
__all__declarations to public submodules (anyio.lowleveletc.) (#1009) - Added the ability to set the token count of a
CapacityLimiterto zero (#1019; requires Python 3.10 or later when using Trio) - Added parameters
case_sensitiveandrecurse_symlinksalong with support for path-like objects toanyio.Path.glob()andanyio.Path.rglob()(#1033; PR by @NorthIsUp) - Dropped
sniffioas a direct dependency and added theget_available_backends()function (#1021) - Fixed
Process.stdin.send()not raisingClosedResourceErrorandBrokenResourceErroron asyncio. Previously, a non-AnyIO exception was raised in such cases (#671; PR by @gschaffner) - Fixed
Process.stdin.send()not checkpointing before writing data on asyncio (#1002; PR by @gschaffner) - Fixed a race condition where cancelling a
FuturefromBlockingPortal.start_task_soon()would sometimes not cancel the async function (#1011; PR by @gschaffner) - Fixed the presence of the pytest plugin causing breakage with older versions of pytest (<= 6.1.2) (#1028; PR by @saper)
- Fixed a rarely occurring
RuntimeError: Set changed size during iterationwhile shutting down the process pool when using the asyncio backend (#985)
4.11.0
- Added support for cancellation reasons (the
reasonparameter toCancelScope.cancel()) (#975) - Bumped the minimum version of Trio to v0.31.0
- Added the ability to enter the event loop from foreign (non-worker) threads by passing the return value of
anyio.lowlevel.current_token()toanyio.from_thread.run()andanyio.from_thread.run_sync()as thetokenkeyword argument (#256) - Added pytest option (
anyio_mode = "auto") to make the pytest plugin automatically handle all async tests (#971) - Added the
anyio.Condition.wait_for()method for feature parity with asyncio (#974) - Changed the default type argument of
anyio.abc.TaskStatusfromAnytoNone(#964) - Fixed TCP listener behavior to guarantee the same ephemeral port is used for all socket listeners when
local_port=0(#857; PR by @11kkw and @agronholm) - Fixed inconsistency between Trio and asyncio where a TCP stream that previously raised a
BrokenResourceErroronsend()would still raiseBrokenResourceErrorafter the stream was closed on asyncio, butClosedResourceErroron Trio. They now both raise aClosedResourceErrorin this scenario. (#671)