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,utf-8-sig), which made the peer decode a stray\ufeffin front of every message after the first (#1405; PR by @feiiiiii5) - Fixed buffered byte streams returning empty bytes when a wrapped object stream received empty messages instead of waiting for data or raising
EndOfStream(#1409; PR by @00200200) - Fixed
BlockingPortalcrashing withInvalidStateErrorwhen a future it returned was cancelled just as the callable was completing (#1410; PR by @adenzhou1350) - Fixed
UNIXSocketStream.send_fds()silently truncating messages on short socket writes (#1422; PR by @AryanRG) - Fixed cancelled TCP receives on the asyncio backend leaving transport reads enabled, allowing incoming data to accumulate while the application was not receiving. (#1423; PR by @Master-Norna)
- Fixed
CapacityLimiter.acquire_on_behalf_of()on asyncio releasing the wrong borrower when the acquiring task was cancelled during the cancel-shielded checkpoint that runs after a non-blocking acquisition