diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-10-28 19:04:04 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-10-28 19:13:08 +0300 |
commit | 556acf22e5b9e62e6806d9f84b88885f0028553d (patch) | |
tree | 9ff7b45fa733ecbf03b0a418737d64710f089ce8 | |
parent | 6fa678ce43d0d76bb4fc164199021ae53b097b8f (diff) | |
download | ydb-556acf22e5b9e62e6806d9f84b88885f0028553d.tar.gz |
Intermediate changes
commit_hash:7f456b0de6f7825696f8277ce8c49657004dce2a
-rw-r--r-- | build/mapping.conf.json | 6 | ||||
-rw-r--r-- | contrib/python/anyio/.dist-info/METADATA | 6 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/_backends/_asyncio.py | 233 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/_backends/_trio.py | 39 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/_core/_fileio.py | 29 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/_core/_signals.py | 6 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/_core/_streams.py | 4 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/_core/_subprocesses.py | 21 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/abc/_eventloop.py | 8 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/abc/_sockets.py | 8 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/from_thread.py | 16 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/pytest_plugin.py | 51 | ||||
-rw-r--r-- | contrib/python/anyio/anyio/streams/tls.py | 11 | ||||
-rw-r--r-- | contrib/python/anyio/ya.make | 2 |
14 files changed, 239 insertions, 201 deletions
diff --git a/build/mapping.conf.json b/build/mapping.conf.json index 2f050bf559..da06c1f807 100644 --- a/build/mapping.conf.json +++ b/build/mapping.conf.json @@ -848,8 +848,11 @@ "6990868751": "https://devtools-registry.s3.yandex.net/6990868751", "6990860705": "https://devtools-registry.s3.yandex.net/6990860705", "6990881789": "https://devtools-registry.s3.yandex.net/6990881789", + "7324461836": "https://devtools-registry.s3.yandex.net/7324461836", "7193803465": "https://devtools-registry.s3.yandex.net/7193803465", + "7324464594": "https://devtools-registry.s3.yandex.net/7324464594", "7193800506": "https://devtools-registry.s3.yandex.net/7193800506", + "7324461714": "https://devtools-registry.s3.yandex.net/7324461714", "7193813071": "https://devtools-registry.s3.yandex.net/7193813071", "3167009386": "https://devtools-registry.s3.yandex.net/3167009386", "3050798466": "https://devtools-registry.s3.yandex.net/3050798466", @@ -1804,8 +1807,11 @@ "6990868751": "none-none-none-sandbox/backup/3527d100-e2d0-4b0e-bb7a-905010853d98/yfm-docs.tar", "6990860705": "none-none-none-sandbox/backup/d386643e-58f8-43e1-8760-341d73801df8/yfm-docs.tar", "6990881789": "none-none-none-sandbox/backup/efc428e5-52a5-4a6f-8f0c-53f1d255efea/yfm-docs.tar", + "7324461836": "none-none-none-service_resources/TASKLET_EXECUTABLE/backup/0541e185-8261-4b07-9149-257f03a9c8ae/yfm-docs.tar", "7193803465": "none-none-none-service_resources/TASKLET_EXECUTABLE/backup/17df2ad2-24bc-49e8-8909-b58685dac393/yfm-docs.tar", + "7324464594": "none-none-none-service_resources/TASKLET_EXECUTABLE/backup/32cc8c74-decd-44a8-bc8c-f8f0d7edfffe/yfm-docs.tar", "7193800506": "none-none-none-service_resources/TASKLET_EXECUTABLE/backup/9be8ed55-d7f8-4029-a7fd-fbfa072b896f/yfm-docs.tar", + "7324461714": "none-none-none-service_resources/TASKLET_EXECUTABLE/backup/b3543418-58d4-4e1c-b2be-43b55b035e91/yfm-docs.tar", "7193813071": "none-none-none-service_resources/TASKLET_EXECUTABLE/backup/b6531a79-b803-4672-a9e9-f9f348009f5f/yfm-docs.tar", "3167009386": "openjdk 11.0.15 vanilla for darwin", "3050798466": "openjdk 11.0.15 vanilla for darwin-arm64", diff --git a/contrib/python/anyio/.dist-info/METADATA b/contrib/python/anyio/.dist-info/METADATA index 747e994c6b..e28bbd52d0 100644 --- a/contrib/python/anyio/.dist-info/METADATA +++ b/contrib/python/anyio/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: anyio -Version: 4.6.0 +Version: 4.6.2 Summary: High level compatibility layer for multiple asynchronous event loop implementations Author-email: Alex Grönholm <alex.gronholm@nextday.fi> License: MIT @@ -15,12 +15,13 @@ Classifier: Framework :: AnyIO Classifier: Typing :: Typed Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 -Requires-Python: >=3.9 +Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: idna >=2.8 @@ -42,6 +43,7 @@ Requires-Dist: pytest >=7.0 ; extra == 'test' Requires-Dist: pytest-mock >=3.6.1 ; extra == 'test' Requires-Dist: trustme ; extra == 'test' Requires-Dist: uvloop >=0.21.0b1 ; (platform_python_implementation == "CPython" and platform_system != "Windows") and extra == 'test' +Requires-Dist: truststore >=0.9.1 ; (python_version >= "3.10") and extra == 'test' Provides-Extra: trio Requires-Dist: trio >=0.26.1 ; extra == 'trio' diff --git a/contrib/python/anyio/anyio/_backends/_asyncio.py b/contrib/python/anyio/anyio/_backends/_asyncio.py index 9342fab818..fa5349a8c2 100644 --- a/contrib/python/anyio/anyio/_backends/_asyncio.py +++ b/contrib/python/anyio/anyio/_backends/_asyncio.py @@ -20,18 +20,9 @@ from asyncio import ( ) from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] from collections import OrderedDict, deque -from collections.abc import ( - AsyncGenerator, - AsyncIterator, - Awaitable, - Callable, - Collection, - Coroutine, - Iterable, - Sequence, -) +from collections.abc import AsyncIterator, Iterable from concurrent.futures import Future -from contextlib import AbstractContextManager, suppress +from contextlib import suppress from contextvars import Context, copy_context from dataclasses import dataclass from functools import partial, wraps @@ -51,7 +42,15 @@ from types import TracebackType from typing import ( IO, Any, + AsyncGenerator, + Awaitable, + Callable, + Collection, + ContextManager, + Coroutine, Optional, + Sequence, + Tuple, TypeVar, cast, ) @@ -359,14 +358,6 @@ def _task_started(task: asyncio.Task) -> bool: # -def is_anyio_cancellation(exc: CancelledError) -> bool: - return ( - bool(exc.args) - and isinstance(exc.args[0], str) - and exc.args[0].startswith("Cancelled by cancel scope ") - ) - - class CancelScope(BaseCancelScope): def __new__( cls, *, deadline: float = math.inf, shield: bool = False @@ -453,77 +444,35 @@ class CancelScope(BaseCancelScope): host_task_state.cancel_scope = self._parent_scope - # Undo all cancellations done by this scope - if self._cancelling is not None: - while self._cancel_calls: - self._cancel_calls -= 1 - if self._host_task.uncancel() <= self._cancelling: - break - - # We only swallow the exception iff it was an AnyIO CancelledError, either - # directly as exc_val or inside an exception group and there are no cancelled - # parent cancel scopes visible to us here - not_swallowed_exceptions = 0 - swallow_exception = False - if exc_val is not None: - for exc in iterate_exceptions(exc_val): - if self._cancel_called and isinstance(exc, CancelledError): - if not (swallow_exception := self._uncancel(exc)): - not_swallowed_exceptions += 1 - else: - not_swallowed_exceptions += 1 - - # Restart the cancellation effort in the closest visible, cancelled parent - # scope if necessary + # Restart the cancellation effort in the closest directly cancelled parent + # scope if this one was shielded self._restart_cancellation_in_parent() - return swallow_exception and not not_swallowed_exceptions - @property - def _effectively_cancelled(self) -> bool: - cancel_scope: CancelScope | None = self - while cancel_scope is not None: - if cancel_scope._cancel_called: - return True - - if cancel_scope.shield: - return False - - cancel_scope = cancel_scope._parent_scope + if self._cancel_called and exc_val is not None: + for exc in iterate_exceptions(exc_val): + if isinstance(exc, CancelledError): + self._cancelled_caught = self._uncancel(exc) + if self._cancelled_caught: + break - return False + return self._cancelled_caught - @property - def _parent_cancellation_is_visible_to_us(self) -> bool: - return ( - self._parent_scope is not None - and not self.shield - and self._parent_scope._effectively_cancelled - ) + return None def _uncancel(self, cancelled_exc: CancelledError) -> bool: - if self._host_task is None: + if sys.version_info < (3, 9) or self._host_task is None: self._cancel_calls = 0 return True - while True: - if is_anyio_cancellation(cancelled_exc): - # Only swallow the cancellation exception if it's an AnyIO cancel - # exception and there are no other cancel scopes down the line pending - # cancellation - self._cancelled_caught = ( - self._effectively_cancelled - and not self._parent_cancellation_is_visible_to_us - ) - return self._cancelled_caught - - # Sometimes third party frameworks catch a CancelledError and raise a new - # one, so as a workaround we have to look at the previous ones in - # __context__ too for a matching cancel message - if isinstance(cancelled_exc.__context__, CancelledError): - cancelled_exc = cancelled_exc.__context__ - continue + # Undo all cancellations done by this scope + if self._cancelling is not None: + while self._cancel_calls: + self._cancel_calls -= 1 + if self._host_task.uncancel() <= self._cancelling: + return True - return False + self._cancel_calls = 0 + return f"Cancelled by cancel scope {id(self):x}" in cancelled_exc.args def _timeout(self) -> None: if self._deadline != math.inf: @@ -547,17 +496,19 @@ class CancelScope(BaseCancelScope): should_retry = False current = current_task() for task in self._tasks: - should_retry = True if task._must_cancel: # type: ignore[attr-defined] continue # The task is eligible for cancellation if it has started + should_retry = True if task is not current and (task is self._host_task or _task_started(task)): waiter = task._fut_waiter # type: ignore[attr-defined] if not isinstance(waiter, asyncio.Future) or not waiter.done(): - task.cancel(f"Cancelled by cancel scope {id(origin):x}") - if task is origin._host_task: - origin._cancel_calls += 1 + origin._cancel_calls += 1 + if sys.version_info >= (3, 9): + task.cancel(f"Cancelled by cancel scope {id(origin):x}") + else: + task.cancel() # Deliver cancellation to child scopes that aren't shielded or running their own # cancellation callbacks @@ -595,6 +546,17 @@ class CancelScope(BaseCancelScope): scope = scope._parent_scope + 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) -> None: if not self._cancel_called: if self._timeout_handle: @@ -701,50 +663,38 @@ class TaskGroup(abc.TaskGroup): exc_val: BaseException | None, exc_tb: TracebackType | None, ) -> bool | None: + ignore_exception = self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) if exc_val is not None: self.cancel_scope.cancel() if not isinstance(exc_val, CancelledError): self._exceptions.append(exc_val) - try: - if self._tasks: - with CancelScope() as wait_scope: - while self._tasks: - try: - await asyncio.wait(self._tasks) - except CancelledError as exc: - # Shield the scope against further cancellation attempts, - # as they're not productive (#695) - wait_scope.shield = True - self.cancel_scope.cancel() - - # Set exc_val from the cancellation exception if it was - # previously unset. However, we should not replace a native - # cancellation exception with one raise by a cancel scope. - if exc_val is None or ( - isinstance(exc_val, CancelledError) - and not is_anyio_cancellation(exc) - ): - exc_val = exc - else: - # If there are no child tasks to wait on, run at least one checkpoint - # anyway - await AsyncIOBackend.cancel_shielded_checkpoint() + cancelled_exc_while_waiting_tasks: CancelledError | None = None + while self._tasks: + try: + await asyncio.wait(self._tasks) + except CancelledError as exc: + # This task was cancelled natively; reraise the CancelledError later + # unless this task was already interrupted by another exception + self.cancel_scope.cancel() + if cancelled_exc_while_waiting_tasks is None: + cancelled_exc_while_waiting_tasks = exc - self._active = False - if self._exceptions: - raise BaseExceptionGroup( - "unhandled errors in a TaskGroup", self._exceptions - ) - elif exc_val: - raise exc_val - except BaseException as exc: - if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__): - return True + self._active = False + if self._exceptions: + raise BaseExceptionGroup( + "unhandled errors in a TaskGroup", self._exceptions + ) - raise + # Raise the CancelledError received while waiting for child tasks to exit, + # unless the context manager itself was previously exited with another + # exception, or if any of the child tasks raised an exception other than + # CancelledError + if cancelled_exc_while_waiting_tasks: + if exc_val is None or ignore_exception: + raise cancelled_exc_while_waiting_tasks - return self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) + return ignore_exception def _spawn( self, @@ -780,7 +730,7 @@ class TaskGroup(abc.TaskGroup): if not isinstance(exc, CancelledError): self._exceptions.append(exc) - if not self.cancel_scope._effectively_cancelled: + if not self.cancel_scope._parent_cancelled(): self.cancel_scope.cancel() else: task_status_future.set_exception(exc) @@ -856,7 +806,7 @@ class TaskGroup(abc.TaskGroup): # Threads # -_Retval_Queue_Type = tuple[Optional[T_Retval], Optional[BaseException]] +_Retval_Queue_Type = Tuple[Optional[T_Retval], Optional[BaseException]] class WorkerThread(Thread): @@ -1005,7 +955,7 @@ class Process(abc.Process): _stderr: StreamReaderWrapper | None async def aclose(self) -> None: - with CancelScope(shield=True) as scope: + with CancelScope(shield=True): if self._stdin: await self._stdin.aclose() if self._stdout: @@ -1013,14 +963,14 @@ class Process(abc.Process): if self._stderr: await self._stderr.aclose() - scope.shield = False - try: - await self.wait() - except BaseException: - scope.shield = True - self.kill() + try: + await self.wait() + except BaseException: + self.kill() + with CancelScope(shield=True): await self.wait() - raise + + raise async def wait(self) -> int: return await self._process.wait() @@ -1731,9 +1681,10 @@ class Lock(BaseLock): self._waiters: deque[tuple[asyncio.Task, asyncio.Future]] = deque() async def acquire(self) -> None: + task = cast(asyncio.Task, current_task()) if self._owner_task is None and not self._waiters: await AsyncIOBackend.checkpoint_if_cancelled() - self._owner_task = current_task() + self._owner_task = task # Unless on the "fast path", yield control of the event loop so that other # tasks can run too @@ -1746,7 +1697,9 @@ class Lock(BaseLock): return - task = cast(asyncio.Task, current_task()) + if self._owner_task == task: + raise RuntimeError("Attempted to acquire an already held Lock") + fut: asyncio.Future[None] = asyncio.Future() item = task, fut self._waiters.append(item) @@ -1762,10 +1715,14 @@ class Lock(BaseLock): self._waiters.remove(item) def acquire_nowait(self) -> None: + task = cast(asyncio.Task, current_task()) if self._owner_task is None and not self._waiters: - self._owner_task = current_task() + self._owner_task = task return + if self._owner_task is task: + raise RuntimeError("Attempted to acquire an already held Lock") + raise WouldBlock def locked(self) -> bool: @@ -2065,7 +2022,9 @@ class AsyncIOTaskInfo(TaskInfo): if task_state := _task_states.get(task): if cancel_scope := task_state.cancel_scope: - return cancel_scope._effectively_cancelled + return cancel_scope.cancel_called or ( + not cancel_scope.shield and cancel_scope._parent_cancelled() + ) return False @@ -2159,7 +2118,7 @@ class TestRunner(abc.TestRunner): ) -> T_Retval: if not self._runner_task: self._send_stream, receive_stream = create_memory_object_stream[ - tuple[Awaitable[Any], asyncio.Future] + Tuple[Awaitable[Any], asyncio.Future] ](1) self._runner_task = self.get_loop().create_task( self._run_tests_and_fixtures(receive_stream) @@ -2521,7 +2480,7 @@ class AsyncIOBackend(AsyncBackend): cls, host: str, port: int, local_address: IPSockAddrType | None = None ) -> abc.SocketStream: transport, protocol = cast( - tuple[asyncio.Transport, StreamProtocol], + Tuple[asyncio.Transport, StreamProtocol], await get_running_loop().create_connection( StreamProtocol, host, port, local_addr=local_address ), @@ -2700,7 +2659,7 @@ class AsyncIOBackend(AsyncBackend): @classmethod def open_signal_receiver( cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: + ) -> ContextManager[AsyncIterator[Signals]]: return _SignalReceiver(signals) @classmethod diff --git a/contrib/python/anyio/anyio/_backends/_trio.py b/contrib/python/anyio/anyio/_backends/_trio.py index de2189ce78..aee974deb6 100644 --- a/contrib/python/anyio/anyio/_backends/_trio.py +++ b/contrib/python/anyio/anyio/_backends/_trio.py @@ -7,18 +7,8 @@ import socket import sys import types import weakref -from collections.abc import ( - AsyncGenerator, - AsyncIterator, - Awaitable, - Callable, - Collection, - Coroutine, - Iterable, - Sequence, -) +from collections.abc import AsyncIterator, Iterable from concurrent.futures import Future -from contextlib import AbstractContextManager from dataclasses import dataclass from functools import partial from io import IOBase @@ -29,8 +19,15 @@ from types import TracebackType from typing import ( IO, Any, + AsyncGenerator, + Awaitable, + Callable, + Collection, + ContextManager, + Coroutine, Generic, NoReturn, + Sequence, TypeVar, cast, overload, @@ -662,9 +659,19 @@ class Lock(BaseLock): self._fast_acquire = fast_acquire self.__original = trio.Lock() + @staticmethod + def _convert_runtime_error_msg(exc: RuntimeError) -> None: + if exc.args == ("attempt to re-acquire an already held Lock",): + exc.args = ("Attempted to acquire an already held Lock",) + async def acquire(self) -> None: if not self._fast_acquire: - await self.__original.acquire() + try: + await self.__original.acquire() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + return # This is the "fast path" where we don't let other tasks run @@ -673,12 +680,18 @@ class Lock(BaseLock): self.__original.acquire_nowait() except trio.WouldBlock: await self.__original._lot.park() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise def acquire_nowait(self) -> None: try: self.__original.acquire_nowait() except trio.WouldBlock: raise WouldBlock from None + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise def locked(self) -> bool: return self.__original.locked() @@ -1276,7 +1289,7 @@ class TrioBackend(AsyncBackend): @classmethod def open_signal_receiver( cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: + ) -> ContextManager[AsyncIterator[Signals]]: return _SignalReceiver(signals) @classmethod diff --git a/contrib/python/anyio/anyio/_core/_fileio.py b/contrib/python/anyio/anyio/_core/_fileio.py index 23ccb0d66f..214a90bfd8 100644 --- a/contrib/python/anyio/anyio/_core/_fileio.py +++ b/contrib/python/anyio/anyio/_core/_fileio.py @@ -3,7 +3,7 @@ from __future__ import annotations import os import pathlib import sys -from collections.abc import AsyncIterator, Callable, Iterable, Iterator, Sequence +from collections.abc import Callable, Iterable, Iterator, Sequence from dataclasses import dataclass from functools import partial from os import PathLike @@ -12,6 +12,7 @@ from typing import ( TYPE_CHECKING, Any, AnyStr, + AsyncIterator, Final, Generic, overload, @@ -217,6 +218,18 @@ class Path: It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for the deprecated :meth:`~pathlib.Path.link_to` method. + Some methods may be unavailable or have limited functionality, based on the Python + version: + + * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) + * :meth:`~pathlib.Path.full_match` (available on Python 3.13 or later) + * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) + * :meth:`~pathlib.Path.match` (the ``case_sensitive`` paramater is only available on + Python 3.13 or later) + * :meth:`~pathlib.Path.relative_to` (the ``walk_up`` parameter is only available on + Python 3.12 or later) + * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later) + Any methods that do disk I/O need to be awaited on. These methods are: * :meth:`~pathlib.Path.absolute` @@ -232,7 +245,10 @@ class Path: * :meth:`~pathlib.Path.is_dir` * :meth:`~pathlib.Path.is_fifo` * :meth:`~pathlib.Path.is_file` + * :meth:`~pathlib.Path.is_junction` * :meth:`~pathlib.Path.is_mount` + * :meth:`~pathlib.Path.is_socket` + * :meth:`~pathlib.Path.is_symlink` * :meth:`~pathlib.Path.lchmod` * :meth:`~pathlib.Path.lstat` * :meth:`~pathlib.Path.mkdir` @@ -243,11 +259,14 @@ class Path: * :meth:`~pathlib.Path.readlink` * :meth:`~pathlib.Path.rename` * :meth:`~pathlib.Path.replace` + * :meth:`~pathlib.Path.resolve` * :meth:`~pathlib.Path.rmdir` * :meth:`~pathlib.Path.samefile` * :meth:`~pathlib.Path.stat` + * :meth:`~pathlib.Path.symlink_to` * :meth:`~pathlib.Path.touch` * :meth:`~pathlib.Path.unlink` + * :meth:`~pathlib.Path.walk` * :meth:`~pathlib.Path.write_bytes` * :meth:`~pathlib.Path.write_text` @@ -385,9 +404,6 @@ class Path: except ValueError: return False - async def is_junction(self) -> bool: - return await to_thread.run_sync(self._path.is_junction) - async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: func = partial(os.chmod, follow_symlinks=follow_symlinks) return await to_thread.run_sync(func, self._path, mode) @@ -447,6 +463,11 @@ class Path: async def is_file(self) -> bool: return await to_thread.run_sync(self._path.is_file, abandon_on_cancel=True) + if sys.version_info >= (3, 12): + + async def is_junction(self) -> bool: + return await to_thread.run_sync(self._path.is_junction) + async def is_mount(self) -> bool: return await to_thread.run_sync( os.path.ismount, self._path, abandon_on_cancel=True diff --git a/contrib/python/anyio/anyio/_core/_signals.py b/contrib/python/anyio/anyio/_core/_signals.py index f3451d302f..115c749bd9 100644 --- a/contrib/python/anyio/anyio/_core/_signals.py +++ b/contrib/python/anyio/anyio/_core/_signals.py @@ -1,15 +1,13 @@ from __future__ import annotations from collections.abc import AsyncIterator -from contextlib import AbstractContextManager from signal import Signals +from typing import ContextManager from ._eventloop import get_async_backend -def open_signal_receiver( - *signals: Signals, -) -> AbstractContextManager[AsyncIterator[Signals]]: +def open_signal_receiver(*signals: Signals) -> ContextManager[AsyncIterator[Signals]]: """ Start receiving operating system signals. diff --git a/contrib/python/anyio/anyio/_core/_streams.py b/contrib/python/anyio/anyio/_core/_streams.py index 6a9814e5a9..aa6b0c222a 100644 --- a/contrib/python/anyio/anyio/_core/_streams.py +++ b/contrib/python/anyio/anyio/_core/_streams.py @@ -1,7 +1,7 @@ from __future__ import annotations import math -from typing import TypeVar +from typing import Tuple, TypeVar from warnings import warn from ..streams.memory import ( @@ -14,7 +14,7 @@ T_Item = TypeVar("T_Item") class create_memory_object_stream( - tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], + Tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], ): """ Create a memory object stream. diff --git a/contrib/python/anyio/anyio/_core/_subprocesses.py b/contrib/python/anyio/anyio/_core/_subprocesses.py index 7ba41a5b03..1ac2d549df 100644 --- a/contrib/python/anyio/anyio/_core/_subprocesses.py +++ b/contrib/python/anyio/anyio/_core/_subprocesses.py @@ -160,25 +160,38 @@ async def open_process( child process prior to the execution of the subprocess. (POSIX only) :param pass_fds: sequence of file descriptors to keep open between the parent and child processes. (POSIX only) - :param user: effective user to run the process as (POSIX only) - :param group: effective group to run the process as (POSIX only) - :param extra_groups: supplementary groups to set in the subprocess (POSIX only) + :param user: effective user to run the process as (Python >= 3.9; POSIX only) + :param group: effective group to run the process as (Python >= 3.9; POSIX only) + :param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9; + POSIX only) :param umask: if not negative, this umask is applied in the child process before - running the given command (POSIX only) + running the given command (Python >= 3.9; POSIX only) :return: an asynchronous process object """ kwargs: dict[str, Any] = {} if user is not None: + if sys.version_info < (3, 9): + raise TypeError("the 'user' argument requires Python 3.9 or later") + kwargs["user"] = user if group is not None: + if sys.version_info < (3, 9): + raise TypeError("the 'group' argument requires Python 3.9 or later") + kwargs["group"] = group if extra_groups is not None: + if sys.version_info < (3, 9): + raise TypeError("the 'extra_groups' argument requires Python 3.9 or later") + kwargs["extra_groups"] = group if umask >= 0: + if sys.version_info < (3, 9): + raise TypeError("the 'umask' argument requires Python 3.9 or later") + kwargs["umask"] = umask return await get_async_backend().open_process( diff --git a/contrib/python/anyio/anyio/abc/_eventloop.py b/contrib/python/anyio/anyio/abc/_eventloop.py index 93d0e9d25b..2c73bb9ffb 100644 --- a/contrib/python/anyio/anyio/abc/_eventloop.py +++ b/contrib/python/anyio/anyio/abc/_eventloop.py @@ -3,8 +3,7 @@ from __future__ import annotations import math import sys from abc import ABCMeta, abstractmethod -from collections.abc import AsyncIterator, Awaitable, Callable, Sequence -from contextlib import AbstractContextManager +from collections.abc import AsyncIterator, Awaitable from os import PathLike from signal import Signals from socket import AddressFamily, SocketKind, socket @@ -12,6 +11,9 @@ from typing import ( IO, TYPE_CHECKING, Any, + Callable, + ContextManager, + Sequence, TypeVar, Union, overload, @@ -350,7 +352,7 @@ class AsyncBackend(metaclass=ABCMeta): @abstractmethod def open_signal_receiver( cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: + ) -> ContextManager[AsyncIterator[Signals]]: pass @classmethod diff --git a/contrib/python/anyio/anyio/abc/_sockets.py b/contrib/python/anyio/anyio/abc/_sockets.py index 1c6a450cdc..b321225a7b 100644 --- a/contrib/python/anyio/anyio/abc/_sockets.py +++ b/contrib/python/anyio/anyio/abc/_sockets.py @@ -8,7 +8,7 @@ from io import IOBase from ipaddress import IPv4Address, IPv6Address from socket import AddressFamily from types import TracebackType -from typing import Any, TypeVar, Union +from typing import Any, Tuple, TypeVar, Union from .._core._typedattr import ( TypedAttributeProvider, @@ -19,10 +19,10 @@ from ._streams import ByteStream, Listener, UnreliableObjectStream from ._tasks import TaskGroup IPAddressType = Union[str, IPv4Address, IPv6Address] -IPSockAddrType = tuple[str, int] +IPSockAddrType = Tuple[str, int] SockAddrType = Union[IPSockAddrType, str] -UDPPacketType = tuple[bytes, IPSockAddrType] -UNIXDatagramPacketType = tuple[bytes, str] +UDPPacketType = Tuple[bytes, IPSockAddrType] +UNIXDatagramPacketType = Tuple[bytes, str] T_Retval = TypeVar("T_Retval") diff --git a/contrib/python/anyio/anyio/from_thread.py b/contrib/python/anyio/anyio/from_thread.py index 93a4cfe8e4..b8785845ba 100644 --- a/contrib/python/anyio/anyio/from_thread.py +++ b/contrib/python/anyio/anyio/from_thread.py @@ -3,17 +3,15 @@ from __future__ import annotations import sys from collections.abc import Awaitable, Callable, Generator from concurrent.futures import Future -from contextlib import ( - AbstractAsyncContextManager, - AbstractContextManager, - contextmanager, -) +from contextlib import AbstractContextManager, contextmanager from dataclasses import dataclass, field from inspect import isawaitable from threading import Lock, Thread, get_ident from types import TracebackType from typing import ( Any, + AsyncContextManager, + ContextManager, Generic, TypeVar, cast, @@ -89,9 +87,7 @@ class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager): type[BaseException] | None, BaseException | None, TracebackType | None ] = (None, None, None) - def __init__( - self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal - ): + def __init__(self, async_cm: AsyncContextManager[T_co], portal: BlockingPortal): self._async_cm = async_cm self._portal = portal @@ -378,8 +374,8 @@ class BlockingPortal: return f, task_status_future.result() def wrap_async_context_manager( - self, cm: AbstractAsyncContextManager[T_co] - ) -> AbstractContextManager[T_co]: + self, cm: AsyncContextManager[T_co] + ) -> ContextManager[T_co]: """ Wrap an async context manager as a synchronous context manager via this portal. diff --git a/contrib/python/anyio/anyio/pytest_plugin.py b/contrib/python/anyio/anyio/pytest_plugin.py index c9fe1bde92..b7d9305614 100644 --- a/contrib/python/anyio/anyio/pytest_plugin.py +++ b/contrib/python/anyio/anyio/pytest_plugin.py @@ -1,13 +1,14 @@ from __future__ import annotations import sys -from collections.abc import Iterator +from collections.abc import Generator, Iterator from contextlib import ExitStack, contextmanager -from inspect import isasyncgenfunction, iscoroutinefunction -from typing import Any, cast +from inspect import isasyncgenfunction, iscoroutinefunction, ismethod +from typing import Any, Dict, Tuple, cast import pytest import sniffio +from _pytest.fixtures import SubRequest from _pytest.outcomes import Exit from ._core._eventloop import get_all_backends, get_async_backend @@ -27,7 +28,7 @@ def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]: return backend, {} elif isinstance(backend, tuple) and len(backend) == 2: if isinstance(backend[0], str) and isinstance(backend[1], dict): - return cast(tuple[str, dict[str, Any]], backend) + return cast(Tuple[str, Dict[str, Any]], backend) raise TypeError("anyio_backend must be either a string or tuple of (string, dict)") @@ -70,28 +71,56 @@ def pytest_configure(config: Any) -> None: ) -def pytest_fixture_setup(fixturedef: Any, request: Any) -> None: - def wrapper(*args, anyio_backend, **kwargs): # type: ignore[no-untyped-def] +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]: + def wrapper( + *args: Any, anyio_backend: Any, request: SubRequest, **kwargs: Any + ) -> Any: + # Rebind any fixture methods to the request instance + if ( + request.instance + and ismethod(func) + and type(func.__self__) is type(request.instance) + ): + local_func = func.__func__.__get__(request.instance) + else: + local_func = func + backend_name, backend_options = extract_backend_and_options(anyio_backend) if has_backend_arg: kwargs["anyio_backend"] = anyio_backend + if has_request_arg: + kwargs["request"] = request + with get_runner(backend_name, backend_options) as runner: - if isasyncgenfunction(func): - yield from runner.run_asyncgen_fixture(func, kwargs) + if isasyncgenfunction(local_func): + yield from runner.run_asyncgen_fixture(local_func, kwargs) else: - yield runner.run_fixture(func, kwargs) + yield runner.run_fixture(local_func, kwargs) # Only apply this to coroutine functions and async generator functions in requests # that involve the anyio_backend fixture func = fixturedef.func if isasyncgenfunction(func) or iscoroutinefunction(func): if "anyio_backend" in request.fixturenames: - has_backend_arg = "anyio_backend" in fixturedef.argnames fixturedef.func = wrapper - if not has_backend_arg: + original_argname = fixturedef.argnames + + if not (has_backend_arg := "anyio_backend" in fixturedef.argnames): fixturedef.argnames += ("anyio_backend",) + if not (has_request_arg := "request" in fixturedef.argnames): + fixturedef.argnames += ("request",) + + try: + return (yield) + finally: + fixturedef.func = func + fixturedef.argnames = original_argname + + return (yield) + @pytest.hookimpl(tryfirst=True) def pytest_pycollect_makeitem(collector: Any, name: Any, obj: Any) -> None: diff --git a/contrib/python/anyio/anyio/streams/tls.py b/contrib/python/anyio/anyio/streams/tls.py index 83240b4d35..d01c8e6f4c 100644 --- a/contrib/python/anyio/anyio/streams/tls.py +++ b/contrib/python/anyio/anyio/streams/tls.py @@ -7,7 +7,7 @@ import sys from collections.abc import Callable, Mapping from dataclasses import dataclass from functools import wraps -from typing import Any, TypeVar +from typing import Any, Tuple, TypeVar from .. import ( BrokenResourceError, @@ -25,8 +25,8 @@ else: T_Retval = TypeVar("T_Retval") PosArgsT = TypeVarTuple("PosArgsT") -_PCTRTT = tuple[tuple[str, str], ...] -_PCTRTTT = tuple[_PCTRTT, ...] +_PCTRTT = Tuple[Tuple[str, str], ...] +_PCTRTTT = Tuple[_PCTRTT, ...] class TLSAttribute(TypedAttributeSet): @@ -162,9 +162,8 @@ class TLSStream(ByteStream): except ssl.SSLError as exc: self._read_bio.write_eof() self._write_bio.write_eof() - if ( - isinstance(exc, ssl.SSLEOFError) - or "UNEXPECTED_EOF_WHILE_READING" in exc.strerror + if isinstance(exc, ssl.SSLEOFError) or ( + exc.strerror and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror ): if self.standard_compatible: raise BrokenResourceError from exc diff --git a/contrib/python/anyio/ya.make b/contrib/python/anyio/ya.make index cec445229c..bb56a53ce5 100644 --- a/contrib/python/anyio/ya.make +++ b/contrib/python/anyio/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(4.6.0) +VERSION(4.6.2) LICENSE(MIT) |