You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1925 lines
				
				65 KiB
			
		
		
			
		
	
	
					1925 lines
				
				65 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								import array
							 | 
						||
| 
								 | 
							
								import asyncio
							 | 
						||
| 
								 | 
							
								import concurrent.futures
							 | 
						||
| 
								 | 
							
								import math
							 | 
						||
| 
								 | 
							
								import socket
							 | 
						||
| 
								 | 
							
								import sys
							 | 
						||
| 
								 | 
							
								from asyncio.base_events import _run_until_complete_cb  # type: ignore[attr-defined]
							 | 
						||
| 
								 | 
							
								from collections import OrderedDict, deque
							 | 
						||
| 
								 | 
							
								from concurrent.futures import Future
							 | 
						||
| 
								 | 
							
								from contextvars import Context, copy_context
							 | 
						||
| 
								 | 
							
								from dataclasses import dataclass
							 | 
						||
| 
								 | 
							
								from functools import partial, wraps
							 | 
						||
| 
								 | 
							
								from inspect import (
							 | 
						||
| 
								 | 
							
								    CORO_RUNNING, CORO_SUSPENDED, GEN_RUNNING, GEN_SUSPENDED, getcoroutinestate, getgeneratorstate)
							 | 
						||
| 
								 | 
							
								from io import IOBase
							 | 
						||
| 
								 | 
							
								from os import PathLike
							 | 
						||
| 
								 | 
							
								from queue import Queue
							 | 
						||
| 
								 | 
							
								from socket import AddressFamily, SocketKind
							 | 
						||
| 
								 | 
							
								from threading import Thread
							 | 
						||
| 
								 | 
							
								from types import TracebackType
							 | 
						||
| 
								 | 
							
								from typing import (
							 | 
						||
| 
								 | 
							
								    Any, Awaitable, Callable, Collection, Coroutine, Deque, Dict, Generator, Iterable, List,
							 | 
						||
| 
								 | 
							
								    Mapping, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, cast)
							 | 
						||
| 
								 | 
							
								from weakref import WeakKeyDictionary
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import sniffio
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from .. import CapacityLimiterStatistics, EventStatistics, TaskInfo, abc
							 | 
						||
| 
								 | 
							
								from .._core._compat import DeprecatedAsyncContextManager, DeprecatedAwaitable
							 | 
						||
| 
								 | 
							
								from .._core._eventloop import claim_worker_thread, threadlocals
							 | 
						||
| 
								 | 
							
								from .._core._exceptions import (
							 | 
						||
| 
								 | 
							
								    BrokenResourceError, BusyResourceError, ClosedResourceError, EndOfStream)
							 | 
						||
| 
								 | 
							
								from .._core._exceptions import ExceptionGroup as BaseExceptionGroup
							 | 
						||
| 
								 | 
							
								from .._core._exceptions import WouldBlock
							 | 
						||
| 
								 | 
							
								from .._core._sockets import GetAddrInfoReturnType, convert_ipv6_sockaddr
							 | 
						||
| 
								 | 
							
								from .._core._synchronization import CapacityLimiter as BaseCapacityLimiter
							 | 
						||
| 
								 | 
							
								from .._core._synchronization import Event as BaseEvent
							 | 
						||
| 
								 | 
							
								from .._core._synchronization import ResourceGuard
							 | 
						||
| 
								 | 
							
								from .._core._tasks import CancelScope as BaseCancelScope
							 | 
						||
| 
								 | 
							
								from ..abc import IPSockAddrType, UDPPacketType
							 | 
						||
| 
								 | 
							
								from ..lowlevel import RunVar
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if sys.version_info >= (3, 8):
							 | 
						||
| 
								 | 
							
								    get_coro = asyncio.Task.get_coro
							 | 
						||
| 
								 | 
							
								else:
							 | 
						||
| 
								 | 
							
								    def get_coro(task: asyncio.Task) -> Union[Generator, Awaitable[Any]]:
							 | 
						||
| 
								 | 
							
								        return task._coro
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if sys.version_info >= (3, 7):
							 | 
						||
| 
								 | 
							
								    from asyncio import all_tasks, create_task, current_task, get_running_loop
							 | 
						||
| 
								 | 
							
								    from asyncio import run as native_run
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_task_callbacks(task: asyncio.Task) -> Iterable[Callable]:
							 | 
						||
| 
								 | 
							
								        return [cb for cb, context in task._callbacks]  # type: ignore[attr-defined]
							 | 
						||
| 
								 | 
							
								else:
							 | 
						||
| 
								 | 
							
								    _T = TypeVar('_T')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_task_callbacks(task: asyncio.Task) -> Iterable[Callable]:
							 | 
						||
| 
								 | 
							
								        return task._callbacks
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def native_run(main, *, debug=False):
							 | 
						||
| 
								 | 
							
								        # Snatched from Python 3.7
							 | 
						||
| 
								 | 
							
								        from asyncio import coroutines, events, tasks
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def _cancel_all_tasks(loop):
							 | 
						||
| 
								 | 
							
								            to_cancel = all_tasks(loop)
							 | 
						||
| 
								 | 
							
								            if not to_cancel:
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for task in to_cancel:
							 | 
						||
| 
								 | 
							
								                task.cancel()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            loop.run_until_complete(
							 | 
						||
| 
								 | 
							
								                tasks.gather(*to_cancel, loop=loop, return_exceptions=True))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for task in to_cancel:
							 | 
						||
| 
								 | 
							
								                if task.cancelled():
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                if task.exception() is not None:
							 | 
						||
| 
								 | 
							
								                    loop.call_exception_handler({
							 | 
						||
| 
								 | 
							
								                        'message': 'unhandled exception during asyncio.run() shutdown',
							 | 
						||
| 
								 | 
							
								                        'exception': task.exception(),
							 | 
						||
| 
								 | 
							
								                        'task': task,
							 | 
						||
| 
								 | 
							
								                    })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if events._get_running_loop() is not None:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError(
							 | 
						||
| 
								 | 
							
								                "asyncio.run() cannot be called from a running event loop")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not coroutines.iscoroutine(main):
							 | 
						||
| 
								 | 
							
								            raise ValueError(f"a coroutine was expected, got {main!r}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        loop = events.new_event_loop()
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            events.set_event_loop(loop)
							 | 
						||
| 
								 | 
							
								            loop.set_debug(debug)
							 | 
						||
| 
								 | 
							
								            return loop.run_until_complete(main)
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                _cancel_all_tasks(loop)
							 | 
						||
| 
								 | 
							
								                loop.run_until_complete(loop.shutdown_asyncgens())
							 | 
						||
| 
								 | 
							
								            finally:
							 | 
						||
| 
								 | 
							
								                events.set_event_loop(None)
							 | 
						||
| 
								 | 
							
								                loop.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def create_task(coro: Union[Generator[Any, None, _T], Awaitable[_T]], *,
							 | 
						||
| 
								 | 
							
								                    name: object = None) -> asyncio.Task:
							 | 
						||
| 
								 | 
							
								        return get_running_loop().create_task(coro)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_running_loop() -> asyncio.AbstractEventLoop:
							 | 
						||
| 
								 | 
							
								        loop = asyncio._get_running_loop()
							 | 
						||
| 
								 | 
							
								        if loop is not None:
							 | 
						||
| 
								 | 
							
								            return loop
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError('no running event loop')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def all_tasks(loop: Optional[asyncio.AbstractEventLoop] = None) -> Set[asyncio.Task]:
							 | 
						||
| 
								 | 
							
								        """Return a set of all tasks for the loop."""
							 | 
						||
| 
								 | 
							
								        from asyncio import Task
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if loop is None:
							 | 
						||
| 
								 | 
							
								            loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return {t for t in Task.all_tasks(loop) if not t.done()}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def current_task(loop: Optional[asyncio.AbstractEventLoop] = None) -> Optional[asyncio.Task]:
							 | 
						||
| 
								 | 
							
								        if loop is None:
							 | 
						||
| 
								 | 
							
								            loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return asyncio.Task.current_task(loop)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								T_Retval = TypeVar('T_Retval')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Check whether there is native support for task names in asyncio (3.8+)
							 | 
						||
| 
								 | 
							
								_native_task_names = hasattr(asyncio.Task, 'get_name')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_root_task: RunVar[Optional[asyncio.Task]] = RunVar('_root_task')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def find_root_task() -> asyncio.Task:
							 | 
						||
| 
								 | 
							
								    root_task = _root_task.get(None)
							 | 
						||
| 
								 | 
							
								    if root_task is not None and not root_task.done():
							 | 
						||
| 
								 | 
							
								        return root_task
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Look for a task that has been started via run_until_complete()
							 | 
						||
| 
								 | 
							
								    for task in all_tasks():
							 | 
						||
| 
								 | 
							
								        if task._callbacks and not task.done():
							 | 
						||
| 
								 | 
							
								            for cb in _get_task_callbacks(task):
							 | 
						||
| 
								 | 
							
								                if (cb is _run_until_complete_cb
							 | 
						||
| 
								 | 
							
								                        or getattr(cb, '__module__', None) == 'uvloop.loop'):
							 | 
						||
| 
								 | 
							
								                    _root_task.set(task)
							 | 
						||
| 
								 | 
							
								                    return task
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Look up the topmost task in the AnyIO task tree, if possible
							 | 
						||
| 
								 | 
							
								    task = cast(asyncio.Task, current_task())
							 | 
						||
| 
								 | 
							
								    state = _task_states.get(task)
							 | 
						||
| 
								 | 
							
								    if state:
							 | 
						||
| 
								 | 
							
								        cancel_scope = state.cancel_scope
							 | 
						||
| 
								 | 
							
								        while cancel_scope and cancel_scope._parent_scope is not None:
							 | 
						||
| 
								 | 
							
								            cancel_scope = cancel_scope._parent_scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if cancel_scope is not None:
							 | 
						||
| 
								 | 
							
								            return cast(asyncio.Task, cancel_scope._host_task)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return task
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_callable_name(func: Callable) -> str:
							 | 
						||
| 
								 | 
							
								    module = getattr(func, '__module__', None)
							 | 
						||
| 
								 | 
							
								    qualname = getattr(func, '__qualname__', None)
							 | 
						||
| 
								 | 
							
								    return '.'.join([x for x in (module, qualname) if x])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Event loop
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_run_vars = WeakKeyDictionary()  # type: WeakKeyDictionary[asyncio.AbstractEventLoop, Any]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								current_token = get_running_loop
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _task_started(task: asyncio.Task) -> bool:
							 | 
						||
| 
								 | 
							
								    """Return ``True`` if the task has been started and has not finished."""
							 | 
						||
| 
								 | 
							
								    coro = get_coro(task)
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        return getcoroutinestate(coro) in (CORO_RUNNING, CORO_SUSPENDED)
							 | 
						||
| 
								 | 
							
								    except AttributeError:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return getgeneratorstate(cast(Generator, coro)) in (GEN_RUNNING, GEN_SUSPENDED)
							 | 
						||
| 
								 | 
							
								        except AttributeError:
							 | 
						||
| 
								 | 
							
								            # task coro is async_genenerator_asend https://bugs.python.org/issue37771
							 | 
						||
| 
								 | 
							
								            raise Exception(f"Cannot determine if task {task} has started or not")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _maybe_set_event_loop_policy(policy: Optional[asyncio.AbstractEventLoopPolicy],
							 | 
						||
| 
								 | 
							
								                                 use_uvloop: bool) -> None:
							 | 
						||
| 
								 | 
							
								    # On CPython, use uvloop when possible if no other policy has been given and if not
							 | 
						||
| 
								 | 
							
								    # explicitly disabled
							 | 
						||
| 
								 | 
							
								    if policy is None and use_uvloop and sys.implementation.name == 'cpython':
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            import uvloop
							 | 
						||
| 
								 | 
							
								        except ImportError:
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # Test for missing shutdown_default_executor() (uvloop 0.14.0 and earlier)
							 | 
						||
| 
								 | 
							
								            if (not hasattr(asyncio.AbstractEventLoop, 'shutdown_default_executor')
							 | 
						||
| 
								 | 
							
								                    or hasattr(uvloop.loop.Loop, 'shutdown_default_executor')):
							 | 
						||
| 
								 | 
							
								                policy = uvloop.EventLoopPolicy()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if policy is not None:
							 | 
						||
| 
								 | 
							
								        asyncio.set_event_loop_policy(policy)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def run(func: Callable[..., Awaitable[T_Retval]], *args: object,
							 | 
						||
| 
								 | 
							
								        debug: bool = False, use_uvloop: bool = False,
							 | 
						||
| 
								 | 
							
								        policy: Optional[asyncio.AbstractEventLoopPolicy] = None) -> T_Retval:
							 | 
						||
| 
								 | 
							
								    @wraps(func)
							 | 
						||
| 
								 | 
							
								    async def wrapper() -> T_Retval:
							 | 
						||
| 
								 | 
							
								        task = cast(asyncio.Task, current_task())
							 | 
						||
| 
								 | 
							
								        task_state = TaskState(None, get_callable_name(func), None)
							 | 
						||
| 
								 | 
							
								        _task_states[task] = task_state
							 | 
						||
| 
								 | 
							
								        if _native_task_names:
							 | 
						||
| 
								 | 
							
								            task.set_name(task_state.name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return await func(*args)
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            del _task_states[task]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _maybe_set_event_loop_policy(policy, use_uvloop)
							 | 
						||
| 
								 | 
							
								    return native_run(wrapper(), debug=debug)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Miscellaneous
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								sleep = asyncio.sleep
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Timeouts and cancellation
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CancelledError = asyncio.CancelledError
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class CancelScope(BaseCancelScope):
							 | 
						||
| 
								 | 
							
								    def __new__(cls, *, deadline: float = math.inf, shield: bool = False) -> "CancelScope":
							 | 
						||
| 
								 | 
							
								        return object.__new__(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, deadline: float = math.inf, shield: bool = False):
							 | 
						||
| 
								 | 
							
								        self._deadline = deadline
							 | 
						||
| 
								 | 
							
								        self._shield = shield
							 | 
						||
| 
								 | 
							
								        self._parent_scope: Optional[CancelScope] = None
							 | 
						||
| 
								 | 
							
								        self._cancel_called = False
							 | 
						||
| 
								 | 
							
								        self._active = False
							 | 
						||
| 
								 | 
							
								        self._timeout_handle: Optional[asyncio.TimerHandle] = None
							 | 
						||
| 
								 | 
							
								        self._cancel_handle: Optional[asyncio.Handle] = None
							 | 
						||
| 
								 | 
							
								        self._tasks: Set[asyncio.Task] = set()
							 | 
						||
| 
								 | 
							
								        self._host_task: Optional[asyncio.Task] = None
							 | 
						||
| 
								 | 
							
								        self._timeout_expired = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __enter__(self) -> "CancelScope":
							 | 
						||
| 
								 | 
							
								        if self._active:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError(
							 | 
						||
| 
								 | 
							
								                "Each CancelScope may only be used for a single 'with' block"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._host_task = host_task = cast(asyncio.Task, current_task())
							 | 
						||
| 
								 | 
							
								        self._tasks.add(host_task)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            task_state = _task_states[host_task]
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            task_name = host_task.get_name() if _native_task_names else None
							 | 
						||
| 
								 | 
							
								            task_state = TaskState(None, task_name, self)
							 | 
						||
| 
								 | 
							
								            _task_states[host_task] = task_state
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self._parent_scope = task_state.cancel_scope
							 | 
						||
| 
								 | 
							
								            task_state.cancel_scope = self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._timeout()
							 | 
						||
| 
								 | 
							
								        self._active = True
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException],
							 | 
						||
| 
								 | 
							
								                 exc_tb: Optional[TracebackType]) -> Optional[bool]:
							 | 
						||
| 
								 | 
							
								        if not self._active:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError('This cancel scope is not active')
							 | 
						||
| 
								 | 
							
								        if current_task() is not self._host_task:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError('Attempted to exit cancel scope in a different task than it was '
							 | 
						||
| 
								 | 
							
								                               'entered in')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        assert self._host_task is not None
							 | 
						||
| 
								 | 
							
								        host_task_state = _task_states.get(self._host_task)
							 | 
						||
| 
								 | 
							
								        if host_task_state is None or host_task_state.cancel_scope is not self:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError("Attempted to exit a cancel scope that isn't the current tasks's "
							 | 
						||
| 
								 | 
							
								                               "current cancel scope")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._active = False
							 | 
						||
| 
								 | 
							
								        if self._timeout_handle:
							 | 
						||
| 
								 | 
							
								            self._timeout_handle.cancel()
							 | 
						||
| 
								 | 
							
								            self._timeout_handle = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._tasks.remove(self._host_task)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        host_task_state.cancel_scope = self._parent_scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Restart the cancellation effort in the farthest directly cancelled parent scope if this
							 | 
						||
| 
								 | 
							
								        # one was shielded
							 | 
						||
| 
								 | 
							
								        if self._shield:
							 | 
						||
| 
								 | 
							
								            self._deliver_cancellation_to_parent()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if exc_val is not None:
							 | 
						||
| 
								 | 
							
								            exceptions = exc_val.exceptions if isinstance(exc_val, ExceptionGroup) else [exc_val]
							 | 
						||
| 
								 | 
							
								            if all(isinstance(exc, CancelledError) for exc in exceptions):
							 | 
						||
| 
								 | 
							
								                if self._timeout_expired:
							 | 
						||
| 
								 | 
							
								                    return True
							 | 
						||
| 
								 | 
							
								                elif not self._cancel_called:
							 | 
						||
| 
								 | 
							
								                    # Task was cancelled natively
							 | 
						||
| 
								 | 
							
								                    return None
							 | 
						||
| 
								 | 
							
								                elif not self._parent_cancelled():
							 | 
						||
| 
								 | 
							
								                    # This scope was directly cancelled
							 | 
						||
| 
								 | 
							
								                    return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _timeout(self) -> None:
							 | 
						||
| 
								 | 
							
								        if self._deadline != math.inf:
							 | 
						||
| 
								 | 
							
								            loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								            if loop.time() >= self._deadline:
							 | 
						||
| 
								 | 
							
								                self._timeout_expired = True
							 | 
						||
| 
								 | 
							
								                self.cancel()
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self._timeout_handle = loop.call_at(self._deadline, self._timeout)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _deliver_cancellation(self) -> None:
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Deliver cancellation to directly contained tasks and nested cancel scopes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Schedule another run at the end if we still have tasks eligible for cancellation.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        should_retry = False
							 | 
						||
| 
								 | 
							
								        current = current_task()
							 | 
						||
| 
								 | 
							
								        for task in self._tasks:
							 | 
						||
| 
								 | 
							
								            if task._must_cancel:  # type: ignore[attr-defined]
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # The task is eligible for cancellation if it has started and is not in a cancel
							 | 
						||
| 
								 | 
							
								            # scope shielded from this one
							 | 
						||
| 
								 | 
							
								            cancel_scope = _task_states[task].cancel_scope
							 | 
						||
| 
								 | 
							
								            while cancel_scope is not self:
							 | 
						||
| 
								 | 
							
								                if cancel_scope is None or cancel_scope._shield:
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    cancel_scope = cancel_scope._parent_scope
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                should_retry = True
							 | 
						||
| 
								 | 
							
								                if task is not current and (task is self._host_task or _task_started(task)):
							 | 
						||
| 
								 | 
							
								                    task.cancel()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Schedule another callback if there are still tasks left
							 | 
						||
| 
								 | 
							
								        if should_retry:
							 | 
						||
| 
								 | 
							
								            self._cancel_handle = get_running_loop().call_soon(self._deliver_cancellation)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self._cancel_handle = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _deliver_cancellation_to_parent(self) -> None:
							 | 
						||
| 
								 | 
							
								        """Start cancellation effort in the farthest directly cancelled parent scope"""
							 | 
						||
| 
								 | 
							
								        scope = self._parent_scope
							 | 
						||
| 
								 | 
							
								        scope_to_cancel: Optional[CancelScope] = None
							 | 
						||
| 
								 | 
							
								        while scope is not None:
							 | 
						||
| 
								 | 
							
								            if scope._cancel_called and scope._cancel_handle is None:
							 | 
						||
| 
								 | 
							
								                scope_to_cancel = scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # No point in looking beyond any shielded scope
							 | 
						||
| 
								 | 
							
								            if scope._shield:
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            scope = scope._parent_scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if scope_to_cancel is not None:
							 | 
						||
| 
								 | 
							
								            scope_to_cancel._deliver_cancellation()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parent_cancelled(self) -> bool:
							 | 
						||
| 
								 | 
							
								        # Check whether any parent has been cancelled
							 | 
						||
| 
								 | 
							
								        cancel_scope = self._parent_scope
							 | 
						||
| 
								 | 
							
								        while cancel_scope is not None and not cancel_scope._shield:
							 | 
						||
| 
								 | 
							
								            if cancel_scope._cancel_called:
							 | 
						||
| 
								 | 
							
								                return True
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                cancel_scope = cancel_scope._parent_scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def cancel(self) -> DeprecatedAwaitable:
							 | 
						||
| 
								 | 
							
								        if not self._cancel_called:
							 | 
						||
| 
								 | 
							
								            if self._timeout_handle:
							 | 
						||
| 
								 | 
							
								                self._timeout_handle.cancel()
							 | 
						||
| 
								 | 
							
								                self._timeout_handle = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self._cancel_called = True
							 | 
						||
| 
								 | 
							
								            self._deliver_cancellation()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return DeprecatedAwaitable(self.cancel)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def deadline(self) -> float:
							 | 
						||
| 
								 | 
							
								        return self._deadline
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @deadline.setter
							 | 
						||
| 
								 | 
							
								    def deadline(self, value: float) -> None:
							 | 
						||
| 
								 | 
							
								        self._deadline = float(value)
							 | 
						||
| 
								 | 
							
								        if self._timeout_handle is not None:
							 | 
						||
| 
								 | 
							
								            self._timeout_handle.cancel()
							 | 
						||
| 
								 | 
							
								            self._timeout_handle = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self._active and not self._cancel_called:
							 | 
						||
| 
								 | 
							
								            self._timeout()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def cancel_called(self) -> bool:
							 | 
						||
| 
								 | 
							
								        return self._cancel_called
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def shield(self) -> bool:
							 | 
						||
| 
								 | 
							
								        return self._shield
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @shield.setter
							 | 
						||
| 
								 | 
							
								    def shield(self, value: bool) -> None:
							 | 
						||
| 
								 | 
							
								        if self._shield != value:
							 | 
						||
| 
								 | 
							
								            self._shield = value
							 | 
						||
| 
								 | 
							
								            if not value:
							 | 
						||
| 
								 | 
							
								                self._deliver_cancellation_to_parent()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def checkpoint() -> None:
							 | 
						||
| 
								 | 
							
								    await sleep(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def checkpoint_if_cancelled() -> None:
							 | 
						||
| 
								 | 
							
								    task = current_task()
							 | 
						||
| 
								 | 
							
								    if task is None:
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        cancel_scope = _task_states[task].cancel_scope
							 | 
						||
| 
								 | 
							
								    except KeyError:
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while cancel_scope:
							 | 
						||
| 
								 | 
							
								        if cancel_scope.cancel_called:
							 | 
						||
| 
								 | 
							
								            await sleep(0)
							 | 
						||
| 
								 | 
							
								        elif cancel_scope.shield:
							 | 
						||
| 
								 | 
							
								            break
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            cancel_scope = cancel_scope._parent_scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def cancel_shielded_checkpoint() -> None:
							 | 
						||
| 
								 | 
							
								    with CancelScope(shield=True):
							 | 
						||
| 
								 | 
							
								        await sleep(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def current_effective_deadline() -> float:
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        cancel_scope = _task_states[current_task()].cancel_scope  # type: ignore[index]
							 | 
						||
| 
								 | 
							
								    except KeyError:
							 | 
						||
| 
								 | 
							
								        return math.inf
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    deadline = math.inf
							 | 
						||
| 
								 | 
							
								    while cancel_scope:
							 | 
						||
| 
								 | 
							
								        deadline = min(deadline, cancel_scope.deadline)
							 | 
						||
| 
								 | 
							
								        if cancel_scope.shield:
							 | 
						||
| 
								 | 
							
								            break
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            cancel_scope = cancel_scope._parent_scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return deadline
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def current_time() -> float:
							 | 
						||
| 
								 | 
							
								    return get_running_loop().time()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Task states
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class TaskState:
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Encapsulates auxiliary task information that cannot be added to the Task instance itself
							 | 
						||
| 
								 | 
							
								    because there are no guarantees about its implementation.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    __slots__ = 'parent_id', 'name', 'cancel_scope'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, parent_id: Optional[int], name: Optional[str],
							 | 
						||
| 
								 | 
							
								                 cancel_scope: Optional[CancelScope]):
							 | 
						||
| 
								 | 
							
								        self.parent_id = parent_id
							 | 
						||
| 
								 | 
							
								        self.name = name
							 | 
						||
| 
								 | 
							
								        self.cancel_scope = cancel_scope
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_task_states = WeakKeyDictionary()  # type: WeakKeyDictionary[asyncio.Task, TaskState]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Task groups
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ExceptionGroup(BaseExceptionGroup):
							 | 
						||
| 
								 | 
							
								    def __init__(self, exceptions: List[BaseException]):
							 | 
						||
| 
								 | 
							
								        super().__init__()
							 | 
						||
| 
								 | 
							
								        self.exceptions = exceptions
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _AsyncioTaskStatus(abc.TaskStatus):
							 | 
						||
| 
								 | 
							
								    def __init__(self, future: asyncio.Future, parent_id: int):
							 | 
						||
| 
								 | 
							
								        self._future = future
							 | 
						||
| 
								 | 
							
								        self._parent_id = parent_id
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def started(self, value: object = None) -> None:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self._future.set_result(value)
							 | 
						||
| 
								 | 
							
								        except asyncio.InvalidStateError:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError("called 'started' twice on the same task status") from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        task = cast(asyncio.Task, current_task())
							 | 
						||
| 
								 | 
							
								        _task_states[task].parent_id = self._parent_id
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class TaskGroup(abc.TaskGroup):
							 | 
						||
| 
								 | 
							
								    def __init__(self) -> None:
							 | 
						||
| 
								 | 
							
								        self.cancel_scope: CancelScope = CancelScope()
							 | 
						||
| 
								 | 
							
								        self._active = False
							 | 
						||
| 
								 | 
							
								        self._exceptions: List[BaseException] = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def __aenter__(self) -> "TaskGroup":
							 | 
						||
| 
								 | 
							
								        self.cancel_scope.__enter__()
							 | 
						||
| 
								 | 
							
								        self._active = True
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def __aexit__(self, exc_type: Optional[Type[BaseException]],
							 | 
						||
| 
								 | 
							
								                        exc_val: Optional[BaseException],
							 | 
						||
| 
								 | 
							
								                        exc_tb: Optional[TracebackType]) -> Optional[bool]:
							 | 
						||
| 
								 | 
							
								        ignore_exception = self.cancel_scope.__exit__(exc_type, exc_val, exc_tb)
							 | 
						||
| 
								 | 
							
								        if exc_val is not None:
							 | 
						||
| 
								 | 
							
								            self.cancel_scope.cancel()
							 | 
						||
| 
								 | 
							
								            self._exceptions.append(exc_val)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        while self.cancel_scope._tasks:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                await asyncio.wait(self.cancel_scope._tasks)
							 | 
						||
| 
								 | 
							
								            except asyncio.CancelledError:
							 | 
						||
| 
								 | 
							
								                self.cancel_scope.cancel()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._active = False
							 | 
						||
| 
								 | 
							
								        if not self.cancel_scope._parent_cancelled():
							 | 
						||
| 
								 | 
							
								            exceptions = self._filter_cancellation_errors(self._exceptions)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            exceptions = self._exceptions
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            if len(exceptions) > 1:
							 | 
						||
| 
								 | 
							
								                if all(isinstance(e, CancelledError) and not e.args for e in exceptions):
							 | 
						||
| 
								 | 
							
								                    # Tasks were cancelled natively, without a cancellation message
							 | 
						||
| 
								 | 
							
								                    raise CancelledError
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    raise ExceptionGroup(exceptions)
							 | 
						||
| 
								 | 
							
								            elif exceptions and exceptions[0] is not exc_val:
							 | 
						||
| 
								 | 
							
								                raise exceptions[0]
							 | 
						||
| 
								 | 
							
								        except BaseException as exc:
							 | 
						||
| 
								 | 
							
								            # Clear the context here, as it can only be done in-flight.
							 | 
						||
| 
								 | 
							
								            # If the context is not cleared, it can result in recursive tracebacks (see #145).
							 | 
						||
| 
								 | 
							
								            exc.__context__ = None
							 | 
						||
| 
								 | 
							
								            raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return ignore_exception
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def _filter_cancellation_errors(exceptions: Sequence[BaseException]) -> List[BaseException]:
							 | 
						||
| 
								 | 
							
								        filtered_exceptions: List[BaseException] = []
							 | 
						||
| 
								 | 
							
								        for exc in exceptions:
							 | 
						||
| 
								 | 
							
								            if isinstance(exc, ExceptionGroup):
							 | 
						||
| 
								 | 
							
								                new_exceptions = TaskGroup._filter_cancellation_errors(exc.exceptions)
							 | 
						||
| 
								 | 
							
								                if len(new_exceptions) > 1:
							 | 
						||
| 
								 | 
							
								                    filtered_exceptions.append(exc)
							 | 
						||
| 
								 | 
							
								                elif len(new_exceptions) == 1:
							 | 
						||
| 
								 | 
							
								                    filtered_exceptions.append(new_exceptions[0])
							 | 
						||
| 
								 | 
							
								                elif new_exceptions:
							 | 
						||
| 
								 | 
							
								                    new_exc = ExceptionGroup(new_exceptions)
							 | 
						||
| 
								 | 
							
								                    new_exc.__cause__ = exc.__cause__
							 | 
						||
| 
								 | 
							
								                    new_exc.__context__ = exc.__context__
							 | 
						||
| 
								 | 
							
								                    new_exc.__traceback__ = exc.__traceback__
							 | 
						||
| 
								 | 
							
								                    filtered_exceptions.append(new_exc)
							 | 
						||
| 
								 | 
							
								            elif not isinstance(exc, CancelledError) or exc.args:
							 | 
						||
| 
								 | 
							
								                filtered_exceptions.append(exc)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return filtered_exceptions
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def _run_wrapped_task(
							 | 
						||
| 
								 | 
							
								            self, coro: Coroutine, task_status_future: Optional[asyncio.Future]) -> None:
							 | 
						||
| 
								 | 
							
								        # This is the code path for Python 3.6 and 3.7 on which asyncio freaks out if a task raises
							 | 
						||
| 
								 | 
							
								        # a BaseException.
							 | 
						||
| 
								 | 
							
								        __traceback_hide__ = __tracebackhide__ = True  # noqa: F841
							 | 
						||
| 
								 | 
							
								        task = cast(asyncio.Task, current_task())
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            await coro
							 | 
						||
| 
								 | 
							
								        except BaseException as exc:
							 | 
						||
| 
								 | 
							
								            if task_status_future is None or task_status_future.done():
							 | 
						||
| 
								 | 
							
								                self._exceptions.append(exc)
							 | 
						||
| 
								 | 
							
								                self.cancel_scope.cancel()
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                task_status_future.set_exception(exc)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if task_status_future is not None and not task_status_future.done():
							 | 
						||
| 
								 | 
							
								                task_status_future.set_exception(
							 | 
						||
| 
								 | 
							
								                    RuntimeError('Child exited without calling task_status.started()'))
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            if task in self.cancel_scope._tasks:
							 | 
						||
| 
								 | 
							
								                self.cancel_scope._tasks.remove(task)
							 | 
						||
| 
								 | 
							
								                del _task_states[task]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _spawn(self, func: Callable[..., Coroutine], args: tuple, name: object,
							 | 
						||
| 
								 | 
							
								               task_status_future: Optional[asyncio.Future] = None) -> asyncio.Task:
							 | 
						||
| 
								 | 
							
								        def task_done(_task: asyncio.Task) -> None:
							 | 
						||
| 
								 | 
							
								            # This is the code path for Python 3.8+
							 | 
						||
| 
								 | 
							
								            assert _task in self.cancel_scope._tasks
							 | 
						||
| 
								 | 
							
								            self.cancel_scope._tasks.remove(_task)
							 | 
						||
| 
								 | 
							
								            del _task_states[_task]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                exc = _task.exception()
							 | 
						||
| 
								 | 
							
								            except CancelledError as e:
							 | 
						||
| 
								 | 
							
								                while isinstance(e.__context__, CancelledError):
							 | 
						||
| 
								 | 
							
								                    e = e.__context__
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                exc = e
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if exc is not None:
							 | 
						||
| 
								 | 
							
								                if task_status_future is None or task_status_future.done():
							 | 
						||
| 
								 | 
							
								                    self._exceptions.append(exc)
							 | 
						||
| 
								 | 
							
								                    self.cancel_scope.cancel()
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    task_status_future.set_exception(exc)
							 | 
						||
| 
								 | 
							
								            elif task_status_future is not None and not task_status_future.done():
							 | 
						||
| 
								 | 
							
								                task_status_future.set_exception(
							 | 
						||
| 
								 | 
							
								                    RuntimeError('Child exited without calling task_status.started()'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not self._active:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError('This task group is not active; no new tasks can be started.')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        options = {}
							 | 
						||
| 
								 | 
							
								        name = get_callable_name(func) if name is None else str(name)
							 | 
						||
| 
								 | 
							
								        if _native_task_names:
							 | 
						||
| 
								 | 
							
								            options['name'] = name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        kwargs = {}
							 | 
						||
| 
								 | 
							
								        if task_status_future:
							 | 
						||
| 
								 | 
							
								            parent_id = id(current_task())
							 | 
						||
| 
								 | 
							
								            kwargs['task_status'] = _AsyncioTaskStatus(task_status_future,
							 | 
						||
| 
								 | 
							
								                                                       id(self.cancel_scope._host_task))
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            parent_id = id(self.cancel_scope._host_task)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        coro = func(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								        if not asyncio.iscoroutine(coro):
							 | 
						||
| 
								 | 
							
								            raise TypeError(f'Expected an async function, but {func} appears to be synchronous')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        foreign_coro = not hasattr(coro, 'cr_frame') and not hasattr(coro, 'gi_frame')
							 | 
						||
| 
								 | 
							
								        if foreign_coro or sys.version_info < (3, 8):
							 | 
						||
| 
								 | 
							
								            coro = self._run_wrapped_task(coro, task_status_future)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        task = create_task(coro, **options)
							 | 
						||
| 
								 | 
							
								        if not foreign_coro and sys.version_info >= (3, 8):
							 | 
						||
| 
								 | 
							
								            task.add_done_callback(task_done)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Make the spawned task inherit the task group's cancel scope
							 | 
						||
| 
								 | 
							
								        _task_states[task] = TaskState(parent_id=parent_id, name=name,
							 | 
						||
| 
								 | 
							
								                                       cancel_scope=self.cancel_scope)
							 | 
						||
| 
								 | 
							
								        self.cancel_scope._tasks.add(task)
							 | 
						||
| 
								 | 
							
								        return task
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def start_soon(self, func: Callable[..., Coroutine], *args: object,
							 | 
						||
| 
								 | 
							
								                   name: object = None) -> None:
							 | 
						||
| 
								 | 
							
								        self._spawn(func, args, name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def start(self, func: Callable[..., Coroutine], *args: object,
							 | 
						||
| 
								 | 
							
								                    name: object = None) -> None:
							 | 
						||
| 
								 | 
							
								        future: asyncio.Future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								        task = self._spawn(func, args, name, future)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # If the task raises an exception after sending a start value without a switch point
							 | 
						||
| 
								 | 
							
								        # between, the task group is cancelled and this method never proceeds to process the
							 | 
						||
| 
								 | 
							
								        # completed future. That's why we have to have a shielded cancel scope here.
							 | 
						||
| 
								 | 
							
								        with CancelScope(shield=True):
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                return await future
							 | 
						||
| 
								 | 
							
								            except CancelledError:
							 | 
						||
| 
								 | 
							
								                task.cancel()
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Threads
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_Retval_Queue_Type = Tuple[Optional[T_Retval], Optional[BaseException]]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class WorkerThread(Thread):
							 | 
						||
| 
								 | 
							
								    MAX_IDLE_TIME = 10  # seconds
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, root_task: asyncio.Task, workers: Set['WorkerThread'],
							 | 
						||
| 
								 | 
							
								                 idle_workers: Deque['WorkerThread']):
							 | 
						||
| 
								 | 
							
								        super().__init__(name='AnyIO worker thread')
							 | 
						||
| 
								 | 
							
								        self.root_task = root_task
							 | 
						||
| 
								 | 
							
								        self.workers = workers
							 | 
						||
| 
								 | 
							
								        self.idle_workers = idle_workers
							 | 
						||
| 
								 | 
							
								        self.loop = root_task._loop
							 | 
						||
| 
								 | 
							
								        self.queue: Queue[Union[Tuple[Context, Callable, tuple, asyncio.Future], None]] = Queue(2)
							 | 
						||
| 
								 | 
							
								        self.idle_since = current_time()
							 | 
						||
| 
								 | 
							
								        self.stopping = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _report_result(self, future: asyncio.Future, result: Any,
							 | 
						||
| 
								 | 
							
								                       exc: Optional[BaseException]) -> None:
							 | 
						||
| 
								 | 
							
								        self.idle_since = current_time()
							 | 
						||
| 
								 | 
							
								        if not self.stopping:
							 | 
						||
| 
								 | 
							
								            self.idle_workers.append(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not future.cancelled():
							 | 
						||
| 
								 | 
							
								            if exc is not None:
							 | 
						||
| 
								 | 
							
								                future.set_exception(exc)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                future.set_result(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def run(self) -> None:
							 | 
						||
| 
								 | 
							
								        with claim_worker_thread('asyncio'):
							 | 
						||
| 
								 | 
							
								            threadlocals.loop = self.loop
							 | 
						||
| 
								 | 
							
								            while True:
							 | 
						||
| 
								 | 
							
								                item = self.queue.get()
							 | 
						||
| 
								 | 
							
								                if item is None:
							 | 
						||
| 
								 | 
							
								                    # Shutdown command received
							 | 
						||
| 
								 | 
							
								                    return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                context, func, args, future = item
							 | 
						||
| 
								 | 
							
								                if not future.cancelled():
							 | 
						||
| 
								 | 
							
								                    result = None
							 | 
						||
| 
								 | 
							
								                    exception: Optional[BaseException] = None
							 | 
						||
| 
								 | 
							
								                    try:
							 | 
						||
| 
								 | 
							
								                        result = context.run(func, *args)
							 | 
						||
| 
								 | 
							
								                    except BaseException as exc:
							 | 
						||
| 
								 | 
							
								                        exception = exc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if not self.loop.is_closed():
							 | 
						||
| 
								 | 
							
								                        self.loop.call_soon_threadsafe(
							 | 
						||
| 
								 | 
							
								                            self._report_result, future, result, exception)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                self.queue.task_done()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def stop(self, f: Optional[asyncio.Task] = None) -> None:
							 | 
						||
| 
								 | 
							
								        self.stopping = True
							 | 
						||
| 
								 | 
							
								        self.queue.put_nowait(None)
							 | 
						||
| 
								 | 
							
								        self.workers.discard(self)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self.idle_workers.remove(self)
							 | 
						||
| 
								 | 
							
								        except ValueError:
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_threadpool_idle_workers: RunVar[Deque[WorkerThread]] = RunVar('_threadpool_idle_workers')
							 | 
						||
| 
								 | 
							
								_threadpool_workers: RunVar[Set[WorkerThread]] = RunVar('_threadpool_workers')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def run_sync_in_worker_thread(
							 | 
						||
| 
								 | 
							
								        func: Callable[..., T_Retval], *args: object, cancellable: bool = False,
							 | 
						||
| 
								 | 
							
								        limiter: Optional['CapacityLimiter'] = None) -> T_Retval:
							 | 
						||
| 
								 | 
							
								    await checkpoint()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # If this is the first run in this event loop thread, set up the necessary variables
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        idle_workers = _threadpool_idle_workers.get()
							 | 
						||
| 
								 | 
							
								        workers = _threadpool_workers.get()
							 | 
						||
| 
								 | 
							
								    except LookupError:
							 | 
						||
| 
								 | 
							
								        idle_workers = deque()
							 | 
						||
| 
								 | 
							
								        workers = set()
							 | 
						||
| 
								 | 
							
								        _threadpool_idle_workers.set(idle_workers)
							 | 
						||
| 
								 | 
							
								        _threadpool_workers.set(workers)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async with (limiter or current_default_thread_limiter()):
							 | 
						||
| 
								 | 
							
								        with CancelScope(shield=not cancellable):
							 | 
						||
| 
								 | 
							
								            future: asyncio.Future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								            root_task = find_root_task()
							 | 
						||
| 
								 | 
							
								            if not idle_workers:
							 | 
						||
| 
								 | 
							
								                worker = WorkerThread(root_task, workers, idle_workers)
							 | 
						||
| 
								 | 
							
								                worker.start()
							 | 
						||
| 
								 | 
							
								                workers.add(worker)
							 | 
						||
| 
								 | 
							
								                root_task.add_done_callback(worker.stop)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                worker = idle_workers.pop()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # Prune any other workers that have been idle for MAX_IDLE_TIME seconds or longer
							 | 
						||
| 
								 | 
							
								                now = current_time()
							 | 
						||
| 
								 | 
							
								                while idle_workers:
							 | 
						||
| 
								 | 
							
								                    if now - idle_workers[0].idle_since < WorkerThread.MAX_IDLE_TIME:
							 | 
						||
| 
								 | 
							
								                        break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    expired_worker = idle_workers.popleft()
							 | 
						||
| 
								 | 
							
								                    expired_worker.root_task.remove_done_callback(expired_worker.stop)
							 | 
						||
| 
								 | 
							
								                    expired_worker.stop()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            context = copy_context()
							 | 
						||
| 
								 | 
							
								            context.run(sniffio.current_async_library_cvar.set, None)
							 | 
						||
| 
								 | 
							
								            worker.queue.put_nowait((context, func, args, future))
							 | 
						||
| 
								 | 
							
								            return await future
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def run_sync_from_thread(func: Callable[..., T_Retval], *args: object,
							 | 
						||
| 
								 | 
							
								                         loop: Optional[asyncio.AbstractEventLoop] = None) -> T_Retval:
							 | 
						||
| 
								 | 
							
								    @wraps(func)
							 | 
						||
| 
								 | 
							
								    def wrapper() -> None:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            f.set_result(func(*args))
							 | 
						||
| 
								 | 
							
								        except BaseException as exc:
							 | 
						||
| 
								 | 
							
								            f.set_exception(exc)
							 | 
						||
| 
								 | 
							
								            if not isinstance(exc, Exception):
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    f: concurrent.futures.Future[T_Retval] = Future()
							 | 
						||
| 
								 | 
							
								    loop = loop or threadlocals.loop
							 | 
						||
| 
								 | 
							
								    if sys.version_info < (3, 7):
							 | 
						||
| 
								 | 
							
								        loop.call_soon_threadsafe(copy_context().run, wrapper)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        loop.call_soon_threadsafe(wrapper)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return f.result()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def run_async_from_thread(
							 | 
						||
| 
								 | 
							
								    func: Callable[..., Coroutine[Any, Any, T_Retval]], *args: object
							 | 
						||
| 
								 | 
							
								) -> T_Retval:
							 | 
						||
| 
								 | 
							
								    f: concurrent.futures.Future[T_Retval] = asyncio.run_coroutine_threadsafe(
							 | 
						||
| 
								 | 
							
								        func(*args), threadlocals.loop)
							 | 
						||
| 
								 | 
							
								    return f.result()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class BlockingPortal(abc.BlockingPortal):
							 | 
						||
| 
								 | 
							
								    def __new__(cls) -> "BlockingPortal":
							 | 
						||
| 
								 | 
							
								        return object.__new__(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self) -> None:
							 | 
						||
| 
								 | 
							
								        super().__init__()
							 | 
						||
| 
								 | 
							
								        self._loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _spawn_task_from_thread(self, func: Callable, args: tuple, kwargs: Dict[str, Any],
							 | 
						||
| 
								 | 
							
								                                name: object, future: Future) -> None:
							 | 
						||
| 
								 | 
							
								        run_sync_from_thread(
							 | 
						||
| 
								 | 
							
								            partial(self._task_group.start_soon, name=name), self._call_func, func, args, kwargs,
							 | 
						||
| 
								 | 
							
								            future, loop=self._loop)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Subprocesses
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@dataclass(eq=False)
							 | 
						||
| 
								 | 
							
								class StreamReaderWrapper(abc.ByteReceiveStream):
							 | 
						||
| 
								 | 
							
								    _stream: asyncio.StreamReader
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def receive(self, max_bytes: int = 65536) -> bytes:
							 | 
						||
| 
								 | 
							
								        data = await self._stream.read(max_bytes)
							 | 
						||
| 
								 | 
							
								        if data:
							 | 
						||
| 
								 | 
							
								            return data
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise EndOfStream
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        self._stream.feed_eof()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@dataclass(eq=False)
							 | 
						||
| 
								 | 
							
								class StreamWriterWrapper(abc.ByteSendStream):
							 | 
						||
| 
								 | 
							
								    _stream: asyncio.StreamWriter
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send(self, item: bytes) -> None:
							 | 
						||
| 
								 | 
							
								        self._stream.write(item)
							 | 
						||
| 
								 | 
							
								        await self._stream.drain()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        self._stream.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@dataclass(eq=False)
							 | 
						||
| 
								 | 
							
								class Process(abc.Process):
							 | 
						||
| 
								 | 
							
								    _process: asyncio.subprocess.Process
							 | 
						||
| 
								 | 
							
								    _stdin: Optional[StreamWriterWrapper]
							 | 
						||
| 
								 | 
							
								    _stdout: Optional[StreamReaderWrapper]
							 | 
						||
| 
								 | 
							
								    _stderr: Optional[StreamReaderWrapper]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        if self._stdin:
							 | 
						||
| 
								 | 
							
								            await self._stdin.aclose()
							 | 
						||
| 
								 | 
							
								        if self._stdout:
							 | 
						||
| 
								 | 
							
								            await self._stdout.aclose()
							 | 
						||
| 
								 | 
							
								        if self._stderr:
							 | 
						||
| 
								 | 
							
								            await self._stderr.aclose()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        await self.wait()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def wait(self) -> int:
							 | 
						||
| 
								 | 
							
								        return await self._process.wait()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def terminate(self) -> None:
							 | 
						||
| 
								 | 
							
								        self._process.terminate()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def kill(self) -> None:
							 | 
						||
| 
								 | 
							
								        self._process.kill()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def send_signal(self, signal: int) -> None:
							 | 
						||
| 
								 | 
							
								        self._process.send_signal(signal)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def pid(self) -> int:
							 | 
						||
| 
								 | 
							
								        return self._process.pid
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def returncode(self) -> Optional[int]:
							 | 
						||
| 
								 | 
							
								        return self._process.returncode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def stdin(self) -> Optional[abc.ByteSendStream]:
							 | 
						||
| 
								 | 
							
								        return self._stdin
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def stdout(self) -> Optional[abc.ByteReceiveStream]:
							 | 
						||
| 
								 | 
							
								        return self._stdout
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def stderr(self) -> Optional[abc.ByteReceiveStream]:
							 | 
						||
| 
								 | 
							
								        return self._stderr
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def open_process(command: Union[str, Sequence[str]], *, shell: bool,
							 | 
						||
| 
								 | 
							
								                       stdin: int, stdout: int, stderr: int,
							 | 
						||
| 
								 | 
							
								                       cwd: Union[str, bytes, PathLike, None] = None,
							 | 
						||
| 
								 | 
							
								                       env: Optional[Mapping[str, str]] = None,
							 | 
						||
| 
								 | 
							
								                       start_new_session: bool = False) -> Process:
							 | 
						||
| 
								 | 
							
								    await checkpoint()
							 | 
						||
| 
								 | 
							
								    if shell:
							 | 
						||
| 
								 | 
							
								        process = await asyncio.create_subprocess_shell(
							 | 
						||
| 
								 | 
							
								            command, stdin=stdin, stdout=stdout,  # type: ignore[arg-type]
							 | 
						||
| 
								 | 
							
								            stderr=stderr, cwd=cwd, env=env, start_new_session=start_new_session,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        process = await asyncio.create_subprocess_exec(*command, stdin=stdin, stdout=stdout,
							 | 
						||
| 
								 | 
							
								                                                       stderr=stderr, cwd=cwd, env=env,
							 | 
						||
| 
								 | 
							
								                                                       start_new_session=start_new_session)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    stdin_stream = StreamWriterWrapper(process.stdin) if process.stdin else None
							 | 
						||
| 
								 | 
							
								    stdout_stream = StreamReaderWrapper(process.stdout) if process.stdout else None
							 | 
						||
| 
								 | 
							
								    stderr_stream = StreamReaderWrapper(process.stderr) if process.stderr else None
							 | 
						||
| 
								 | 
							
								    return Process(process, stdin_stream, stdout_stream, stderr_stream)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _forcibly_shutdown_process_pool_on_exit(workers: Set[Process], _task: object) -> None:
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Forcibly shuts down worker processes belonging to this event loop."""
							 | 
						||
| 
								 | 
							
								    child_watcher: Optional[asyncio.AbstractChildWatcher]
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        child_watcher = asyncio.get_event_loop_policy().get_child_watcher()
							 | 
						||
| 
								 | 
							
								    except NotImplementedError:
							 | 
						||
| 
								 | 
							
								        child_watcher = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Close as much as possible (w/o async/await) to avoid warnings
							 | 
						||
| 
								 | 
							
								    for process in workers:
							 | 
						||
| 
								 | 
							
								        if process.returncode is None:
							 | 
						||
| 
								 | 
							
								            continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        process._stdin._stream._transport.close()  # type: ignore[union-attr]
							 | 
						||
| 
								 | 
							
								        process._stdout._stream._transport.close()  # type: ignore[union-attr]
							 | 
						||
| 
								 | 
							
								        process._stderr._stream._transport.close()  # type: ignore[union-attr]
							 | 
						||
| 
								 | 
							
								        process.kill()
							 | 
						||
| 
								 | 
							
								        if child_watcher:
							 | 
						||
| 
								 | 
							
								            child_watcher.remove_child_handler(process.pid)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def _shutdown_process_pool_on_exit(workers: Set[Process]) -> None:
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Shuts down worker processes belonging to this event loop.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    NOTE: this only works when the event loop was started using asyncio.run() or anyio.run().
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    process: Process
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        await sleep(math.inf)
							 | 
						||
| 
								 | 
							
								    except asyncio.CancelledError:
							 | 
						||
| 
								 | 
							
								        for process in workers:
							 | 
						||
| 
								 | 
							
								            if process.returncode is None:
							 | 
						||
| 
								 | 
							
								                process.kill()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for process in workers:
							 | 
						||
| 
								 | 
							
								            await process.aclose()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def setup_process_pool_exit_at_shutdown(workers: Set[Process]) -> None:
							 | 
						||
| 
								 | 
							
								    kwargs = {'name': 'AnyIO process pool shutdown task'} if _native_task_names else {}
							 | 
						||
| 
								 | 
							
								    create_task(_shutdown_process_pool_on_exit(workers), **kwargs)
							 | 
						||
| 
								 | 
							
								    find_root_task().add_done_callback(partial(_forcibly_shutdown_process_pool_on_exit, workers))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Sockets and networking
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class StreamProtocol(asyncio.Protocol):
							 | 
						||
| 
								 | 
							
								    read_queue: Deque[bytes]
							 | 
						||
| 
								 | 
							
								    read_event: asyncio.Event
							 | 
						||
| 
								 | 
							
								    write_event: asyncio.Event
							 | 
						||
| 
								 | 
							
								    exception: Optional[Exception] = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def connection_made(self, transport: asyncio.BaseTransport) -> None:
							 | 
						||
| 
								 | 
							
								        self.read_queue = deque()
							 | 
						||
| 
								 | 
							
								        self.read_event = asyncio.Event()
							 | 
						||
| 
								 | 
							
								        self.write_event = asyncio.Event()
							 | 
						||
| 
								 | 
							
								        self.write_event.set()
							 | 
						||
| 
								 | 
							
								        cast(asyncio.Transport, transport).set_write_buffer_limits(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def connection_lost(self, exc: Optional[Exception]) -> None:
							 | 
						||
| 
								 | 
							
								        if exc:
							 | 
						||
| 
								 | 
							
								            self.exception = BrokenResourceError()
							 | 
						||
| 
								 | 
							
								            self.exception.__cause__ = exc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.read_event.set()
							 | 
						||
| 
								 | 
							
								        self.write_event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def data_received(self, data: bytes) -> None:
							 | 
						||
| 
								 | 
							
								        self.read_queue.append(data)
							 | 
						||
| 
								 | 
							
								        self.read_event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def eof_received(self) -> Optional[bool]:
							 | 
						||
| 
								 | 
							
								        self.read_event.set()
							 | 
						||
| 
								 | 
							
								        return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def pause_writing(self) -> None:
							 | 
						||
| 
								 | 
							
								        self.write_event = asyncio.Event()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def resume_writing(self) -> None:
							 | 
						||
| 
								 | 
							
								        self.write_event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class DatagramProtocol(asyncio.DatagramProtocol):
							 | 
						||
| 
								 | 
							
								    read_queue: Deque[Tuple[bytes, IPSockAddrType]]
							 | 
						||
| 
								 | 
							
								    read_event: asyncio.Event
							 | 
						||
| 
								 | 
							
								    write_event: asyncio.Event
							 | 
						||
| 
								 | 
							
								    exception: Optional[Exception] = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def connection_made(self, transport: asyncio.BaseTransport) -> None:
							 | 
						||
| 
								 | 
							
								        self.read_queue = deque(maxlen=100)  # arbitrary value
							 | 
						||
| 
								 | 
							
								        self.read_event = asyncio.Event()
							 | 
						||
| 
								 | 
							
								        self.write_event = asyncio.Event()
							 | 
						||
| 
								 | 
							
								        self.write_event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def connection_lost(self, exc: Optional[Exception]) -> None:
							 | 
						||
| 
								 | 
							
								        self.read_event.set()
							 | 
						||
| 
								 | 
							
								        self.write_event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def datagram_received(self, data: bytes, addr: IPSockAddrType) -> None:
							 | 
						||
| 
								 | 
							
								        addr = convert_ipv6_sockaddr(addr)
							 | 
						||
| 
								 | 
							
								        self.read_queue.append((data, addr))
							 | 
						||
| 
								 | 
							
								        self.read_event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def error_received(self, exc: Exception) -> None:
							 | 
						||
| 
								 | 
							
								        self.exception = exc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def pause_writing(self) -> None:
							 | 
						||
| 
								 | 
							
								        self.write_event.clear()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def resume_writing(self) -> None:
							 | 
						||
| 
								 | 
							
								        self.write_event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class SocketStream(abc.SocketStream):
							 | 
						||
| 
								 | 
							
								    def __init__(self, transport: asyncio.Transport, protocol: StreamProtocol):
							 | 
						||
| 
								 | 
							
								        self._transport = transport
							 | 
						||
| 
								 | 
							
								        self._protocol = protocol
							 | 
						||
| 
								 | 
							
								        self._receive_guard = ResourceGuard('reading from')
							 | 
						||
| 
								 | 
							
								        self._send_guard = ResourceGuard('writing to')
							 | 
						||
| 
								 | 
							
								        self._closed = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _raw_socket(self) -> socket.socket:
							 | 
						||
| 
								 | 
							
								        return self._transport.get_extra_info('socket')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def receive(self, max_bytes: int = 65536) -> bytes:
							 | 
						||
| 
								 | 
							
								        with self._receive_guard:
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if not self._protocol.read_event.is_set() and not self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								                self._transport.resume_reading()
							 | 
						||
| 
								 | 
							
								                await self._protocol.read_event.wait()
							 | 
						||
| 
								 | 
							
								                self._transport.pause_reading()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                chunk = self._protocol.read_queue.popleft()
							 | 
						||
| 
								 | 
							
								            except IndexError:
							 | 
						||
| 
								 | 
							
								                if self._closed:
							 | 
						||
| 
								 | 
							
								                    raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                elif self._protocol.exception:
							 | 
						||
| 
								 | 
							
								                    raise self._protocol.exception
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    raise EndOfStream from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if len(chunk) > max_bytes:
							 | 
						||
| 
								 | 
							
								                # Split the oversized chunk
							 | 
						||
| 
								 | 
							
								                chunk, leftover = chunk[:max_bytes], chunk[max_bytes:]
							 | 
						||
| 
								 | 
							
								                self._protocol.read_queue.appendleft(leftover)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # If the read queue is empty, clear the flag so that the next call will block until
							 | 
						||
| 
								 | 
							
								            # data is available
							 | 
						||
| 
								 | 
							
								            if not self._protocol.read_queue:
							 | 
						||
| 
								 | 
							
								                self._protocol.read_event.clear()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return chunk
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send(self, item: bytes) -> None:
							 | 
						||
| 
								 | 
							
								        with self._send_guard:
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if self._closed:
							 | 
						||
| 
								 | 
							
								                raise ClosedResourceError
							 | 
						||
| 
								 | 
							
								            elif self._protocol.exception is not None:
							 | 
						||
| 
								 | 
							
								                raise self._protocol.exception
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                self._transport.write(item)
							 | 
						||
| 
								 | 
							
								            except RuntimeError as exc:
							 | 
						||
| 
								 | 
							
								                if self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								                    raise BrokenResourceError from exc
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            await self._protocol.write_event.wait()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send_eof(self) -> None:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self._transport.write_eof()
							 | 
						||
| 
								 | 
							
								        except OSError:
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        if not self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								            self._closed = True
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                self._transport.write_eof()
							 | 
						||
| 
								 | 
							
								            except OSError:
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self._transport.close()
							 | 
						||
| 
								 | 
							
								            await sleep(0)
							 | 
						||
| 
								 | 
							
								            self._transport.abort()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class UNIXSocketStream(abc.SocketStream):
							 | 
						||
| 
								 | 
							
								    _receive_future: Optional[asyncio.Future] = None
							 | 
						||
| 
								 | 
							
								    _send_future: Optional[asyncio.Future] = None
							 | 
						||
| 
								 | 
							
								    _closing = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, raw_socket: socket.socket):
							 | 
						||
| 
								 | 
							
								        self.__raw_socket = raw_socket
							 | 
						||
| 
								 | 
							
								        self._loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								        self._receive_guard = ResourceGuard('reading from')
							 | 
						||
| 
								 | 
							
								        self._send_guard = ResourceGuard('writing to')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _raw_socket(self) -> socket.socket:
							 | 
						||
| 
								 | 
							
								        return self.__raw_socket
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _wait_until_readable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future:
							 | 
						||
| 
								 | 
							
								        def callback(f: object) -> None:
							 | 
						||
| 
								 | 
							
								            del self._receive_future
							 | 
						||
| 
								 | 
							
								            loop.remove_reader(self.__raw_socket)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        f = self._receive_future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								        self._loop.add_reader(self.__raw_socket, f.set_result, None)
							 | 
						||
| 
								 | 
							
								        f.add_done_callback(callback)
							 | 
						||
| 
								 | 
							
								        return f
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _wait_until_writable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future:
							 | 
						||
| 
								 | 
							
								        def callback(f: object) -> None:
							 | 
						||
| 
								 | 
							
								            del self._send_future
							 | 
						||
| 
								 | 
							
								            loop.remove_writer(self.__raw_socket)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        f = self._send_future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								        self._loop.add_writer(self.__raw_socket, f.set_result, None)
							 | 
						||
| 
								 | 
							
								        f.add_done_callback(callback)
							 | 
						||
| 
								 | 
							
								        return f
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send_eof(self) -> None:
							 | 
						||
| 
								 | 
							
								        with self._send_guard:
							 | 
						||
| 
								 | 
							
								            self._raw_socket.shutdown(socket.SHUT_WR)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def receive(self, max_bytes: int = 65536) -> bytes:
							 | 
						||
| 
								 | 
							
								        loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								        await checkpoint()
							 | 
						||
| 
								 | 
							
								        with self._receive_guard:
							 | 
						||
| 
								 | 
							
								            while True:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    data = self.__raw_socket.recv(max_bytes)
							 | 
						||
| 
								 | 
							
								                except BlockingIOError:
							 | 
						||
| 
								 | 
							
								                    await self._wait_until_readable(loop)
							 | 
						||
| 
								 | 
							
								                except OSError as exc:
							 | 
						||
| 
								 | 
							
								                    if self._closing:
							 | 
						||
| 
								 | 
							
								                        raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        raise BrokenResourceError from exc
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    if not data:
							 | 
						||
| 
								 | 
							
								                        raise EndOfStream
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    return data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send(self, item: bytes) -> None:
							 | 
						||
| 
								 | 
							
								        loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								        await checkpoint()
							 | 
						||
| 
								 | 
							
								        with self._send_guard:
							 | 
						||
| 
								 | 
							
								            view = memoryview(item)
							 | 
						||
| 
								 | 
							
								            while view:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    bytes_sent = self.__raw_socket.send(item)
							 | 
						||
| 
								 | 
							
								                except BlockingIOError:
							 | 
						||
| 
								 | 
							
								                    await self._wait_until_writable(loop)
							 | 
						||
| 
								 | 
							
								                except OSError as exc:
							 | 
						||
| 
								 | 
							
								                    if self._closing:
							 | 
						||
| 
								 | 
							
								                        raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        raise BrokenResourceError from exc
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    view = view[bytes_sent:]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def receive_fds(self, msglen: int, maxfds: int) -> Tuple[bytes, List[int]]:
							 | 
						||
| 
								 | 
							
								        if not isinstance(msglen, int) or msglen < 0:
							 | 
						||
| 
								 | 
							
								            raise ValueError('msglen must be a non-negative integer')
							 | 
						||
| 
								 | 
							
								        if not isinstance(maxfds, int) or maxfds < 1:
							 | 
						||
| 
								 | 
							
								            raise ValueError('maxfds must be a positive integer')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								        fds = array.array("i")
							 | 
						||
| 
								 | 
							
								        await checkpoint()
							 | 
						||
| 
								 | 
							
								        with self._receive_guard:
							 | 
						||
| 
								 | 
							
								            while True:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    message, ancdata, flags, addr = self.__raw_socket.recvmsg(
							 | 
						||
| 
								 | 
							
								                        msglen, socket.CMSG_LEN(maxfds * fds.itemsize))
							 | 
						||
| 
								 | 
							
								                except BlockingIOError:
							 | 
						||
| 
								 | 
							
								                    await self._wait_until_readable(loop)
							 | 
						||
| 
								 | 
							
								                except OSError as exc:
							 | 
						||
| 
								 | 
							
								                    if self._closing:
							 | 
						||
| 
								 | 
							
								                        raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        raise BrokenResourceError from exc
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    if not message and not ancdata:
							 | 
						||
| 
								 | 
							
								                        raise EndOfStream
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for cmsg_level, cmsg_type, cmsg_data in ancdata:
							 | 
						||
| 
								 | 
							
								            if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS:
							 | 
						||
| 
								 | 
							
								                raise RuntimeError(f'Received unexpected ancillary data; message = {message!r}, '
							 | 
						||
| 
								 | 
							
								                                   f'cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return message, list(fds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send_fds(self, message: bytes, fds: Collection[Union[int, IOBase]]) -> None:
							 | 
						||
| 
								 | 
							
								        if not message:
							 | 
						||
| 
								 | 
							
								            raise ValueError('message must not be empty')
							 | 
						||
| 
								 | 
							
								        if not fds:
							 | 
						||
| 
								 | 
							
								            raise ValueError('fds must not be empty')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								        filenos: List[int] = []
							 | 
						||
| 
								 | 
							
								        for fd in fds:
							 | 
						||
| 
								 | 
							
								            if isinstance(fd, int):
							 | 
						||
| 
								 | 
							
								                filenos.append(fd)
							 | 
						||
| 
								 | 
							
								            elif isinstance(fd, IOBase):
							 | 
						||
| 
								 | 
							
								                filenos.append(fd.fileno())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        fdarray = array.array("i", filenos)
							 | 
						||
| 
								 | 
							
								        await checkpoint()
							 | 
						||
| 
								 | 
							
								        with self._send_guard:
							 | 
						||
| 
								 | 
							
								            while True:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    # The ignore can be removed after mypy picks up
							 | 
						||
| 
								 | 
							
								                    # https://github.com/python/typeshed/pull/5545
							 | 
						||
| 
								 | 
							
								                    self.__raw_socket.sendmsg(
							 | 
						||
| 
								 | 
							
								                        [message],
							 | 
						||
| 
								 | 
							
								                        [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fdarray)]
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								                except BlockingIOError:
							 | 
						||
| 
								 | 
							
								                    await self._wait_until_writable(loop)
							 | 
						||
| 
								 | 
							
								                except OSError as exc:
							 | 
						||
| 
								 | 
							
								                    if self._closing:
							 | 
						||
| 
								 | 
							
								                        raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        raise BrokenResourceError from exc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        if not self._closing:
							 | 
						||
| 
								 | 
							
								            self._closing = True
							 | 
						||
| 
								 | 
							
								            if self.__raw_socket.fileno() != -1:
							 | 
						||
| 
								 | 
							
								                self.__raw_socket.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if self._receive_future:
							 | 
						||
| 
								 | 
							
								                self._receive_future.set_result(None)
							 | 
						||
| 
								 | 
							
								            if self._send_future:
							 | 
						||
| 
								 | 
							
								                self._send_future.set_result(None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class TCPSocketListener(abc.SocketListener):
							 | 
						||
| 
								 | 
							
								    _accept_scope: Optional[CancelScope] = None
							 | 
						||
| 
								 | 
							
								    _closed = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, raw_socket: socket.socket):
							 | 
						||
| 
								 | 
							
								        self.__raw_socket = raw_socket
							 | 
						||
| 
								 | 
							
								        self._loop = cast(asyncio.BaseEventLoop, get_running_loop())
							 | 
						||
| 
								 | 
							
								        self._accept_guard = ResourceGuard('accepting connections from')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _raw_socket(self) -> socket.socket:
							 | 
						||
| 
								 | 
							
								        return self.__raw_socket
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def accept(self) -> abc.SocketStream:
							 | 
						||
| 
								 | 
							
								        if self._closed:
							 | 
						||
| 
								 | 
							
								            raise ClosedResourceError
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with self._accept_guard:
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								            with CancelScope() as self._accept_scope:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    client_sock, _addr = await self._loop.sock_accept(self._raw_socket)
							 | 
						||
| 
								 | 
							
								                except asyncio.CancelledError:
							 | 
						||
| 
								 | 
							
								                    # Workaround for https://bugs.python.org/issue41317
							 | 
						||
| 
								 | 
							
								                    try:
							 | 
						||
| 
								 | 
							
								                        self._loop.remove_reader(self._raw_socket)
							 | 
						||
| 
								 | 
							
								                    except (ValueError, NotImplementedError):
							 | 
						||
| 
								 | 
							
								                        pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if self._closed:
							 | 
						||
| 
								 | 
							
								                        raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    raise
							 | 
						||
| 
								 | 
							
								                finally:
							 | 
						||
| 
								 | 
							
								                    self._accept_scope = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
							 | 
						||
| 
								 | 
							
								        transport, protocol = await self._loop.connect_accepted_socket(StreamProtocol, client_sock)
							 | 
						||
| 
								 | 
							
								        return SocketStream(cast(asyncio.Transport, transport), cast(StreamProtocol, protocol))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        if self._closed:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._closed = True
							 | 
						||
| 
								 | 
							
								        if self._accept_scope:
							 | 
						||
| 
								 | 
							
								            # Workaround for https://bugs.python.org/issue41317
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                self._loop.remove_reader(self._raw_socket)
							 | 
						||
| 
								 | 
							
								            except (ValueError, NotImplementedError):
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self._accept_scope.cancel()
							 | 
						||
| 
								 | 
							
								            await sleep(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._raw_socket.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class UNIXSocketListener(abc.SocketListener):
							 | 
						||
| 
								 | 
							
								    def __init__(self, raw_socket: socket.socket):
							 | 
						||
| 
								 | 
							
								        self.__raw_socket = raw_socket
							 | 
						||
| 
								 | 
							
								        self._loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								        self._accept_guard = ResourceGuard('accepting connections from')
							 | 
						||
| 
								 | 
							
								        self._closed = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def accept(self) -> abc.SocketStream:
							 | 
						||
| 
								 | 
							
								        await checkpoint()
							 | 
						||
| 
								 | 
							
								        with self._accept_guard:
							 | 
						||
| 
								 | 
							
								            while True:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    client_sock, _ = self.__raw_socket.accept()
							 | 
						||
| 
								 | 
							
								                    client_sock.setblocking(False)
							 | 
						||
| 
								 | 
							
								                    return UNIXSocketStream(client_sock)
							 | 
						||
| 
								 | 
							
								                except BlockingIOError:
							 | 
						||
| 
								 | 
							
								                    f: asyncio.Future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								                    self._loop.add_reader(self.__raw_socket, f.set_result, None)
							 | 
						||
| 
								 | 
							
								                    f.add_done_callback(lambda _: self._loop.remove_reader(self.__raw_socket))
							 | 
						||
| 
								 | 
							
								                    await f
							 | 
						||
| 
								 | 
							
								                except OSError as exc:
							 | 
						||
| 
								 | 
							
								                    if self._closed:
							 | 
						||
| 
								 | 
							
								                        raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        raise BrokenResourceError from exc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        self._closed = True
							 | 
						||
| 
								 | 
							
								        self.__raw_socket.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _raw_socket(self) -> socket.socket:
							 | 
						||
| 
								 | 
							
								        return self.__raw_socket
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class UDPSocket(abc.UDPSocket):
							 | 
						||
| 
								 | 
							
								    def __init__(self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol):
							 | 
						||
| 
								 | 
							
								        self._transport = transport
							 | 
						||
| 
								 | 
							
								        self._protocol = protocol
							 | 
						||
| 
								 | 
							
								        self._receive_guard = ResourceGuard('reading from')
							 | 
						||
| 
								 | 
							
								        self._send_guard = ResourceGuard('writing to')
							 | 
						||
| 
								 | 
							
								        self._closed = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _raw_socket(self) -> socket.socket:
							 | 
						||
| 
								 | 
							
								        return self._transport.get_extra_info('socket')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        if not self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								            self._closed = True
							 | 
						||
| 
								 | 
							
								            self._transport.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def receive(self) -> Tuple[bytes, IPSockAddrType]:
							 | 
						||
| 
								 | 
							
								        with self._receive_guard:
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # If the buffer is empty, ask for more data
							 | 
						||
| 
								 | 
							
								            if not self._protocol.read_queue and not self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								                self._protocol.read_event.clear()
							 | 
						||
| 
								 | 
							
								                await self._protocol.read_event.wait()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                return self._protocol.read_queue.popleft()
							 | 
						||
| 
								 | 
							
								            except IndexError:
							 | 
						||
| 
								 | 
							
								                if self._closed:
							 | 
						||
| 
								 | 
							
								                    raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    raise BrokenResourceError from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send(self, item: UDPPacketType) -> None:
							 | 
						||
| 
								 | 
							
								        with self._send_guard:
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								            await self._protocol.write_event.wait()
							 | 
						||
| 
								 | 
							
								            if self._closed:
							 | 
						||
| 
								 | 
							
								                raise ClosedResourceError
							 | 
						||
| 
								 | 
							
								            elif self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								                raise BrokenResourceError
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self._transport.sendto(*item)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ConnectedUDPSocket(abc.ConnectedUDPSocket):
							 | 
						||
| 
								 | 
							
								    def __init__(self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol):
							 | 
						||
| 
								 | 
							
								        self._transport = transport
							 | 
						||
| 
								 | 
							
								        self._protocol = protocol
							 | 
						||
| 
								 | 
							
								        self._receive_guard = ResourceGuard('reading from')
							 | 
						||
| 
								 | 
							
								        self._send_guard = ResourceGuard('writing to')
							 | 
						||
| 
								 | 
							
								        self._closed = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _raw_socket(self) -> socket.socket:
							 | 
						||
| 
								 | 
							
								        return self._transport.get_extra_info('socket')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def aclose(self) -> None:
							 | 
						||
| 
								 | 
							
								        if not self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								            self._closed = True
							 | 
						||
| 
								 | 
							
								            self._transport.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def receive(self) -> bytes:
							 | 
						||
| 
								 | 
							
								        with self._receive_guard:
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # If the buffer is empty, ask for more data
							 | 
						||
| 
								 | 
							
								            if not self._protocol.read_queue and not self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								                self._protocol.read_event.clear()
							 | 
						||
| 
								 | 
							
								                await self._protocol.read_event.wait()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                packet = self._protocol.read_queue.popleft()
							 | 
						||
| 
								 | 
							
								            except IndexError:
							 | 
						||
| 
								 | 
							
								                if self._closed:
							 | 
						||
| 
								 | 
							
								                    raise ClosedResourceError from None
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    raise BrokenResourceError from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return packet[0]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def send(self, item: bytes) -> None:
							 | 
						||
| 
								 | 
							
								        with self._send_guard:
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								            await self._protocol.write_event.wait()
							 | 
						||
| 
								 | 
							
								            if self._closed:
							 | 
						||
| 
								 | 
							
								                raise ClosedResourceError
							 | 
						||
| 
								 | 
							
								            elif self._transport.is_closing():
							 | 
						||
| 
								 | 
							
								                raise BrokenResourceError
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self._transport.sendto(item)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def connect_tcp(host: str, port: int,
							 | 
						||
| 
								 | 
							
								                      local_addr: Optional[Tuple[str, int]] = None) -> SocketStream:
							 | 
						||
| 
								 | 
							
								    transport, protocol = cast(
							 | 
						||
| 
								 | 
							
								        Tuple[asyncio.Transport, StreamProtocol],
							 | 
						||
| 
								 | 
							
								        await get_running_loop().create_connection(StreamProtocol, host, port,
							 | 
						||
| 
								 | 
							
								                                                   local_addr=local_addr)
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    transport.pause_reading()
							 | 
						||
| 
								 | 
							
								    return SocketStream(transport, protocol)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def connect_unix(path: str) -> UNIXSocketStream:
							 | 
						||
| 
								 | 
							
								    await checkpoint()
							 | 
						||
| 
								 | 
							
								    loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								    raw_socket = socket.socket(socket.AF_UNIX)
							 | 
						||
| 
								 | 
							
								    raw_socket.setblocking(False)
							 | 
						||
| 
								 | 
							
								    while True:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            raw_socket.connect(path)
							 | 
						||
| 
								 | 
							
								        except BlockingIOError:
							 | 
						||
| 
								 | 
							
								            f: asyncio.Future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								            loop.add_writer(raw_socket, f.set_result, None)
							 | 
						||
| 
								 | 
							
								            f.add_done_callback(lambda _: loop.remove_writer(raw_socket))
							 | 
						||
| 
								 | 
							
								            await f
							 | 
						||
| 
								 | 
							
								        except BaseException:
							 | 
						||
| 
								 | 
							
								            raw_socket.close()
							 | 
						||
| 
								 | 
							
								            raise
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return UNIXSocketStream(raw_socket)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def create_udp_socket(
							 | 
						||
| 
								 | 
							
								    family: socket.AddressFamily,
							 | 
						||
| 
								 | 
							
								    local_address: Optional[IPSockAddrType],
							 | 
						||
| 
								 | 
							
								    remote_address: Optional[IPSockAddrType],
							 | 
						||
| 
								 | 
							
								    reuse_port: bool
							 | 
						||
| 
								 | 
							
								) -> Union[UDPSocket, ConnectedUDPSocket]:
							 | 
						||
| 
								 | 
							
								    result = await get_running_loop().create_datagram_endpoint(
							 | 
						||
| 
								 | 
							
								        DatagramProtocol, local_addr=local_address, remote_addr=remote_address, family=family,
							 | 
						||
| 
								 | 
							
								        reuse_port=reuse_port)
							 | 
						||
| 
								 | 
							
								    transport = cast(asyncio.DatagramTransport, result[0])
							 | 
						||
| 
								 | 
							
								    protocol = cast(DatagramProtocol, result[1])
							 | 
						||
| 
								 | 
							
								    if protocol.exception:
							 | 
						||
| 
								 | 
							
								        transport.close()
							 | 
						||
| 
								 | 
							
								        raise protocol.exception
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if not remote_address:
							 | 
						||
| 
								 | 
							
								        return UDPSocket(transport, protocol)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        return ConnectedUDPSocket(transport, protocol)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def getaddrinfo(host: Union[bytearray, bytes, str], port: Union[str, int, None], *,
							 | 
						||
| 
								 | 
							
								                      family: Union[int, AddressFamily] = 0, type: Union[int, SocketKind] = 0,
							 | 
						||
| 
								 | 
							
								                      proto: int = 0, flags: int = 0) -> GetAddrInfoReturnType:
							 | 
						||
| 
								 | 
							
								    # https://github.com/python/typeshed/pull/4304
							 | 
						||
| 
								 | 
							
								    result = await get_running_loop().getaddrinfo(
							 | 
						||
| 
								 | 
							
								        host, port, family=family, type=type, proto=proto, flags=flags)  # type: ignore[arg-type]
							 | 
						||
| 
								 | 
							
								    return cast(GetAddrInfoReturnType, result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Tuple[str, str]:
							 | 
						||
| 
								 | 
							
								    return await get_running_loop().getnameinfo(sockaddr, flags)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_read_events: RunVar[Dict[Any, asyncio.Event]] = RunVar('read_events')
							 | 
						||
| 
								 | 
							
								_write_events: RunVar[Dict[Any, asyncio.Event]] = RunVar('write_events')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def wait_socket_readable(sock: socket.socket) -> None:
							 | 
						||
| 
								 | 
							
								    await checkpoint()
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        read_events = _read_events.get()
							 | 
						||
| 
								 | 
							
								    except LookupError:
							 | 
						||
| 
								 | 
							
								        read_events = {}
							 | 
						||
| 
								 | 
							
								        _read_events.set(read_events)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if read_events.get(sock):
							 | 
						||
| 
								 | 
							
								        raise BusyResourceError('reading from') from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								    event = read_events[sock] = asyncio.Event()
							 | 
						||
| 
								 | 
							
								    loop.add_reader(sock, event.set)
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        await event.wait()
							 | 
						||
| 
								 | 
							
								    finally:
							 | 
						||
| 
								 | 
							
								        if read_events.pop(sock, None) is not None:
							 | 
						||
| 
								 | 
							
								            loop.remove_reader(sock)
							 | 
						||
| 
								 | 
							
								            readable = True
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            readable = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if not readable:
							 | 
						||
| 
								 | 
							
								        raise ClosedResourceError
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def wait_socket_writable(sock: socket.socket) -> None:
							 | 
						||
| 
								 | 
							
								    await checkpoint()
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        write_events = _write_events.get()
							 | 
						||
| 
								 | 
							
								    except LookupError:
							 | 
						||
| 
								 | 
							
								        write_events = {}
							 | 
						||
| 
								 | 
							
								        _write_events.set(write_events)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if write_events.get(sock):
							 | 
						||
| 
								 | 
							
								        raise BusyResourceError('writing to') from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								    event = write_events[sock] = asyncio.Event()
							 | 
						||
| 
								 | 
							
								    loop.add_writer(sock.fileno(), event.set)
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        await event.wait()
							 | 
						||
| 
								 | 
							
								    finally:
							 | 
						||
| 
								 | 
							
								        if write_events.pop(sock, None) is not None:
							 | 
						||
| 
								 | 
							
								            loop.remove_writer(sock)
							 | 
						||
| 
								 | 
							
								            writable = True
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            writable = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if not writable:
							 | 
						||
| 
								 | 
							
								        raise ClosedResourceError
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Synchronization
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Event(BaseEvent):
							 | 
						||
| 
								 | 
							
								    def __new__(cls) -> "Event":
							 | 
						||
| 
								 | 
							
								        return object.__new__(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self) -> None:
							 | 
						||
| 
								 | 
							
								        self._event = asyncio.Event()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def set(self) -> DeprecatedAwaitable:
							 | 
						||
| 
								 | 
							
								        self._event.set()
							 | 
						||
| 
								 | 
							
								        return DeprecatedAwaitable(self.set)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def is_set(self) -> bool:
							 | 
						||
| 
								 | 
							
								        return self._event.is_set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def wait(self) -> None:
							 | 
						||
| 
								 | 
							
								        if await self._event.wait():
							 | 
						||
| 
								 | 
							
								            await checkpoint()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def statistics(self) -> EventStatistics:
							 | 
						||
| 
								 | 
							
								        return EventStatistics(len(self._event._waiters))  # type: ignore[attr-defined]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class CapacityLimiter(BaseCapacityLimiter):
							 | 
						||
| 
								 | 
							
								    _total_tokens: float = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __new__(cls, total_tokens: float) -> "CapacityLimiter":
							 | 
						||
| 
								 | 
							
								        return object.__new__(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, total_tokens: float):
							 | 
						||
| 
								 | 
							
								        self._borrowers: Set[Any] = set()
							 | 
						||
| 
								 | 
							
								        self._wait_queue: Dict[Any, asyncio.Event] = OrderedDict()
							 | 
						||
| 
								 | 
							
								        self.total_tokens = total_tokens
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def __aenter__(self) -> None:
							 | 
						||
| 
								 | 
							
								        await self.acquire()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def __aexit__(self, exc_type: Optional[Type[BaseException]],
							 | 
						||
| 
								 | 
							
								                        exc_val: Optional[BaseException],
							 | 
						||
| 
								 | 
							
								                        exc_tb: Optional[TracebackType]) -> None:
							 | 
						||
| 
								 | 
							
								        self.release()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def total_tokens(self) -> float:
							 | 
						||
| 
								 | 
							
								        return self._total_tokens
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @total_tokens.setter
							 | 
						||
| 
								 | 
							
								    def total_tokens(self, value: float) -> None:
							 | 
						||
| 
								 | 
							
								        if not isinstance(value, int) and not math.isinf(value):
							 | 
						||
| 
								 | 
							
								            raise TypeError('total_tokens must be an int or math.inf')
							 | 
						||
| 
								 | 
							
								        if value < 1:
							 | 
						||
| 
								 | 
							
								            raise ValueError('total_tokens must be >= 1')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        old_value = self._total_tokens
							 | 
						||
| 
								 | 
							
								        self._total_tokens = value
							 | 
						||
| 
								 | 
							
								        events = []
							 | 
						||
| 
								 | 
							
								        for event in self._wait_queue.values():
							 | 
						||
| 
								 | 
							
								            if value <= old_value:
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if not event.is_set():
							 | 
						||
| 
								 | 
							
								                events.append(event)
							 | 
						||
| 
								 | 
							
								                old_value += 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for event in events:
							 | 
						||
| 
								 | 
							
								            event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def borrowed_tokens(self) -> int:
							 | 
						||
| 
								 | 
							
								        return len(self._borrowers)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def available_tokens(self) -> float:
							 | 
						||
| 
								 | 
							
								        return self._total_tokens - len(self._borrowers)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def acquire_nowait(self) -> DeprecatedAwaitable:
							 | 
						||
| 
								 | 
							
								        self.acquire_on_behalf_of_nowait(current_task())
							 | 
						||
| 
								 | 
							
								        return DeprecatedAwaitable(self.acquire_nowait)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def acquire_on_behalf_of_nowait(self, borrower: object) -> DeprecatedAwaitable:
							 | 
						||
| 
								 | 
							
								        if borrower in self._borrowers:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError("this borrower is already holding one of this CapacityLimiter's "
							 | 
						||
| 
								 | 
							
								                               "tokens")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self._wait_queue or len(self._borrowers) >= self._total_tokens:
							 | 
						||
| 
								 | 
							
								            raise WouldBlock
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._borrowers.add(borrower)
							 | 
						||
| 
								 | 
							
								        return DeprecatedAwaitable(self.acquire_on_behalf_of_nowait)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def acquire(self) -> None:
							 | 
						||
| 
								 | 
							
								        return await self.acquire_on_behalf_of(current_task())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def acquire_on_behalf_of(self, borrower: object) -> None:
							 | 
						||
| 
								 | 
							
								        await checkpoint_if_cancelled()
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self.acquire_on_behalf_of_nowait(borrower)
							 | 
						||
| 
								 | 
							
								        except WouldBlock:
							 | 
						||
| 
								 | 
							
								            event = asyncio.Event()
							 | 
						||
| 
								 | 
							
								            self._wait_queue[borrower] = event
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                await event.wait()
							 | 
						||
| 
								 | 
							
								            except BaseException:
							 | 
						||
| 
								 | 
							
								                self._wait_queue.pop(borrower, None)
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self._borrowers.add(borrower)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                await cancel_shielded_checkpoint()
							 | 
						||
| 
								 | 
							
								            except BaseException:
							 | 
						||
| 
								 | 
							
								                self.release()
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def release(self) -> None:
							 | 
						||
| 
								 | 
							
								        self.release_on_behalf_of(current_task())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def release_on_behalf_of(self, borrower: object) -> None:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self._borrowers.remove(borrower)
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            raise RuntimeError("this borrower isn't holding any of this CapacityLimiter's "
							 | 
						||
| 
								 | 
							
								                               "tokens") from None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Notify the next task in line if this limiter has free capacity now
							 | 
						||
| 
								 | 
							
								        if self._wait_queue and len(self._borrowers) < self._total_tokens:
							 | 
						||
| 
								 | 
							
								            event = self._wait_queue.popitem()[1]
							 | 
						||
| 
								 | 
							
								            event.set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def statistics(self) -> CapacityLimiterStatistics:
							 | 
						||
| 
								 | 
							
								        return CapacityLimiterStatistics(self.borrowed_tokens, self.total_tokens,
							 | 
						||
| 
								 | 
							
								                                         tuple(self._borrowers), len(self._wait_queue))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_default_thread_limiter: RunVar[CapacityLimiter] = RunVar('_default_thread_limiter')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def current_default_thread_limiter() -> CapacityLimiter:
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        return _default_thread_limiter.get()
							 | 
						||
| 
								 | 
							
								    except LookupError:
							 | 
						||
| 
								 | 
							
								        limiter = CapacityLimiter(40)
							 | 
						||
| 
								 | 
							
								        _default_thread_limiter.set(limiter)
							 | 
						||
| 
								 | 
							
								        return limiter
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Operating system signals
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _SignalReceiver(DeprecatedAsyncContextManager["_SignalReceiver"]):
							 | 
						||
| 
								 | 
							
								    def __init__(self, signals: Tuple[int, ...]):
							 | 
						||
| 
								 | 
							
								        self._signals = signals
							 | 
						||
| 
								 | 
							
								        self._loop = get_running_loop()
							 | 
						||
| 
								 | 
							
								        self._signal_queue: Deque[int] = deque()
							 | 
						||
| 
								 | 
							
								        self._future: asyncio.Future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								        self._handled_signals: Set[int] = set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _deliver(self, signum: int) -> None:
							 | 
						||
| 
								 | 
							
								        self._signal_queue.append(signum)
							 | 
						||
| 
								 | 
							
								        if not self._future.done():
							 | 
						||
| 
								 | 
							
								            self._future.set_result(None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __enter__(self) -> "_SignalReceiver":
							 | 
						||
| 
								 | 
							
								        for sig in set(self._signals):
							 | 
						||
| 
								 | 
							
								            self._loop.add_signal_handler(sig, self._deliver, sig)
							 | 
						||
| 
								 | 
							
								            self._handled_signals.add(sig)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __exit__(self, exc_type: Optional[Type[BaseException]],
							 | 
						||
| 
								 | 
							
								                 exc_val: Optional[BaseException],
							 | 
						||
| 
								 | 
							
								                 exc_tb: Optional[TracebackType]) -> Optional[bool]:
							 | 
						||
| 
								 | 
							
								        for sig in self._handled_signals:
							 | 
						||
| 
								 | 
							
								            self._loop.remove_signal_handler(sig)
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __aiter__(self) -> "_SignalReceiver":
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def __anext__(self) -> int:
							 | 
						||
| 
								 | 
							
								        await checkpoint()
							 | 
						||
| 
								 | 
							
								        if not self._signal_queue:
							 | 
						||
| 
								 | 
							
								            self._future = asyncio.Future()
							 | 
						||
| 
								 | 
							
								            await self._future
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self._signal_queue.popleft()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def open_signal_receiver(*signals: int) -> _SignalReceiver:
							 | 
						||
| 
								 | 
							
								    return _SignalReceiver(signals)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Testing and debugging
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _create_task_info(task: asyncio.Task) -> TaskInfo:
							 | 
						||
| 
								 | 
							
								    task_state = _task_states.get(task)
							 | 
						||
| 
								 | 
							
								    if task_state is None:
							 | 
						||
| 
								 | 
							
								        name = task.get_name() if _native_task_names else None
							 | 
						||
| 
								 | 
							
								        parent_id = None
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        name = task_state.name
							 | 
						||
| 
								 | 
							
								        parent_id = task_state.parent_id
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return TaskInfo(id(task), parent_id, name, get_coro(task))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_current_task() -> TaskInfo:
							 | 
						||
| 
								 | 
							
								    return _create_task_info(current_task())  # type: ignore[arg-type]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_running_tasks() -> List[TaskInfo]:
							 | 
						||
| 
								 | 
							
								    return [_create_task_info(task) for task in all_tasks() if not task.done()]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								async def wait_all_tasks_blocked() -> None:
							 | 
						||
| 
								 | 
							
								    await checkpoint()
							 | 
						||
| 
								 | 
							
								    this_task = current_task()
							 | 
						||
| 
								 | 
							
								    while True:
							 | 
						||
| 
								 | 
							
								        for task in all_tasks():
							 | 
						||
| 
								 | 
							
								            if task is this_task:
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if task._fut_waiter is None or task._fut_waiter.done():  # type: ignore[attr-defined]
							 | 
						||
| 
								 | 
							
								                await sleep(0.1)
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class TestRunner(abc.TestRunner):
							 | 
						||
| 
								 | 
							
								    def __init__(self, debug: bool = False, use_uvloop: bool = False,
							 | 
						||
| 
								 | 
							
								                 policy: Optional[asyncio.AbstractEventLoopPolicy] = None):
							 | 
						||
| 
								 | 
							
								        _maybe_set_event_loop_policy(policy, use_uvloop)
							 | 
						||
| 
								 | 
							
								        self._loop = asyncio.new_event_loop()
							 | 
						||
| 
								 | 
							
								        self._loop.set_debug(debug)
							 | 
						||
| 
								 | 
							
								        asyncio.set_event_loop(self._loop)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _cancel_all_tasks(self) -> None:
							 | 
						||
| 
								 | 
							
								        to_cancel = all_tasks(self._loop)
							 | 
						||
| 
								 | 
							
								        if not to_cancel:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for task in to_cancel:
							 | 
						||
| 
								 | 
							
								            task.cancel()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for task in to_cancel:
							 | 
						||
| 
								 | 
							
								            if task.cancelled():
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            if task.exception() is not None:
							 | 
						||
| 
								 | 
							
								                raise cast(BaseException, task.exception())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def close(self) -> None:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self._cancel_all_tasks()
							 | 
						||
| 
								 | 
							
								            self._loop.run_until_complete(self._loop.shutdown_asyncgens())
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            asyncio.set_event_loop(None)
							 | 
						||
| 
								 | 
							
								            self._loop.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def call(self, func: Callable[..., Awaitable[T_Retval]],
							 | 
						||
| 
								 | 
							
								             *args: object, **kwargs: object) -> T_Retval:
							 | 
						||
| 
								 | 
							
								        def exception_handler(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None:
							 | 
						||
| 
								 | 
							
								            exceptions.append(context['exception'])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        exceptions: List[BaseException] = []
							 | 
						||
| 
								 | 
							
								        self._loop.set_exception_handler(exception_handler)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            retval: T_Retval = self._loop.run_until_complete(func(*args, **kwargs))
							 | 
						||
| 
								 | 
							
								        except Exception as exc:
							 | 
						||
| 
								 | 
							
								            retval = None  # type: ignore[assignment]
							 | 
						||
| 
								 | 
							
								            exceptions.append(exc)
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            self._loop.set_exception_handler(None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if len(exceptions) == 1:
							 | 
						||
| 
								 | 
							
								            raise exceptions[0]
							 | 
						||
| 
								 | 
							
								        elif exceptions:
							 | 
						||
| 
								 | 
							
								            raise ExceptionGroup(exceptions)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return retval
							 |