mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Проверка 09.02.2025
This commit is contained in:
@@ -8,6 +8,7 @@ from ._core._eventloop import sleep as sleep
|
||||
from ._core._eventloop import sleep_forever as sleep_forever
|
||||
from ._core._eventloop import sleep_until as sleep_until
|
||||
from ._core._exceptions import BrokenResourceError as BrokenResourceError
|
||||
from ._core._exceptions import BrokenWorkerIntepreter as BrokenWorkerIntepreter
|
||||
from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess
|
||||
from ._core._exceptions import BusyResourceError as BusyResourceError
|
||||
from ._core._exceptions import ClosedResourceError as ClosedResourceError
|
||||
|
@@ -28,8 +28,6 @@ from collections.abc import (
|
||||
Collection,
|
||||
Coroutine,
|
||||
Iterable,
|
||||
Iterator,
|
||||
MutableMapping,
|
||||
Sequence,
|
||||
)
|
||||
from concurrent.futures import Future
|
||||
@@ -49,7 +47,7 @@ from queue import Queue
|
||||
from signal import Signals
|
||||
from socket import AddressFamily, SocketKind
|
||||
from threading import Thread
|
||||
from types import TracebackType
|
||||
from types import CodeType, TracebackType
|
||||
from typing import (
|
||||
IO,
|
||||
TYPE_CHECKING,
|
||||
@@ -449,7 +447,7 @@ class CancelScope(BaseCancelScope):
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> bool | None:
|
||||
) -> bool:
|
||||
del exc_tb
|
||||
|
||||
if not self._active:
|
||||
@@ -677,45 +675,7 @@ class TaskState:
|
||||
self.cancel_scope = cancel_scope
|
||||
|
||||
|
||||
class TaskStateStore(MutableMapping["Awaitable[Any] | asyncio.Task", TaskState]):
|
||||
def __init__(self) -> None:
|
||||
self._task_states = WeakKeyDictionary[asyncio.Task, TaskState]()
|
||||
self._preliminary_task_states: dict[Awaitable[Any], TaskState] = {}
|
||||
|
||||
def __getitem__(self, key: Awaitable[Any] | asyncio.Task, /) -> TaskState:
|
||||
assert isinstance(key, asyncio.Task)
|
||||
try:
|
||||
return self._task_states[key]
|
||||
except KeyError:
|
||||
if coro := key.get_coro():
|
||||
if state := self._preliminary_task_states.get(coro):
|
||||
return state
|
||||
|
||||
raise KeyError(key)
|
||||
|
||||
def __setitem__(
|
||||
self, key: asyncio.Task | Awaitable[Any], value: TaskState, /
|
||||
) -> None:
|
||||
if isinstance(key, asyncio.Task):
|
||||
self._task_states[key] = value
|
||||
else:
|
||||
self._preliminary_task_states[key] = value
|
||||
|
||||
def __delitem__(self, key: asyncio.Task | Awaitable[Any], /) -> None:
|
||||
if isinstance(key, asyncio.Task):
|
||||
del self._task_states[key]
|
||||
else:
|
||||
del self._preliminary_task_states[key]
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._task_states) + len(self._preliminary_task_states)
|
||||
|
||||
def __iter__(self) -> Iterator[Awaitable[Any] | asyncio.Task]:
|
||||
yield from self._task_states
|
||||
yield from self._preliminary_task_states
|
||||
|
||||
|
||||
_task_states = TaskStateStore()
|
||||
_task_states: WeakKeyDictionary[asyncio.Task, TaskState] = WeakKeyDictionary()
|
||||
|
||||
|
||||
#
|
||||
@@ -741,24 +701,10 @@ class _AsyncioTaskStatus(abc.TaskStatus):
|
||||
_task_states[task].parent_id = self._parent_id
|
||||
|
||||
|
||||
async def _wait(tasks: Iterable[asyncio.Task[object]]) -> None:
|
||||
tasks = set(tasks)
|
||||
waiter = get_running_loop().create_future()
|
||||
|
||||
def on_completion(task: asyncio.Task[object]) -> None:
|
||||
tasks.discard(task)
|
||||
if not tasks and not waiter.done():
|
||||
waiter.set_result(None)
|
||||
|
||||
for task in tasks:
|
||||
task.add_done_callback(on_completion)
|
||||
del task
|
||||
|
||||
try:
|
||||
await waiter
|
||||
finally:
|
||||
while tasks:
|
||||
tasks.pop().remove_done_callback(on_completion)
|
||||
if sys.version_info >= (3, 12):
|
||||
_eager_task_factory_code: CodeType | None = asyncio.eager_task_factory.__code__
|
||||
else:
|
||||
_eager_task_factory_code = None
|
||||
|
||||
|
||||
class TaskGroup(abc.TaskGroup):
|
||||
@@ -767,6 +713,7 @@ class TaskGroup(abc.TaskGroup):
|
||||
self._active = False
|
||||
self._exceptions: list[BaseException] = []
|
||||
self._tasks: set[asyncio.Task] = set()
|
||||
self._on_completed_fut: asyncio.Future[None] | None = None
|
||||
|
||||
async def __aenter__(self) -> TaskGroup:
|
||||
self.cancel_scope.__enter__()
|
||||
@@ -785,12 +732,15 @@ class TaskGroup(abc.TaskGroup):
|
||||
if not isinstance(exc_val, CancelledError):
|
||||
self._exceptions.append(exc_val)
|
||||
|
||||
loop = get_running_loop()
|
||||
try:
|
||||
if self._tasks:
|
||||
with CancelScope() as wait_scope:
|
||||
while self._tasks:
|
||||
self._on_completed_fut = loop.create_future()
|
||||
|
||||
try:
|
||||
await _wait(self._tasks)
|
||||
await self._on_completed_fut
|
||||
except CancelledError as exc:
|
||||
# Shield the scope against further cancellation attempts,
|
||||
# as they're not productive (#695)
|
||||
@@ -805,6 +755,8 @@ class TaskGroup(abc.TaskGroup):
|
||||
and not is_anyio_cancellation(exc)
|
||||
):
|
||||
exc_val = exc
|
||||
|
||||
self._on_completed_fut = None
|
||||
else:
|
||||
# If there are no child tasks to wait on, run at least one checkpoint
|
||||
# anyway
|
||||
@@ -835,13 +787,19 @@ class TaskGroup(abc.TaskGroup):
|
||||
task_status_future: asyncio.Future | None = None,
|
||||
) -> asyncio.Task:
|
||||
def task_done(_task: asyncio.Task) -> None:
|
||||
# task_state = _task_states[_task]
|
||||
task_state = _task_states[_task]
|
||||
assert task_state.cancel_scope is not None
|
||||
assert _task in task_state.cancel_scope._tasks
|
||||
task_state.cancel_scope._tasks.remove(_task)
|
||||
self._tasks.remove(task)
|
||||
del _task_states[_task]
|
||||
|
||||
if self._on_completed_fut is not None and not self._tasks:
|
||||
try:
|
||||
self._on_completed_fut.set_result(None)
|
||||
except asyncio.InvalidStateError:
|
||||
pass
|
||||
|
||||
try:
|
||||
exc = _task.exception()
|
||||
except CancelledError as e:
|
||||
@@ -892,26 +850,25 @@ class TaskGroup(abc.TaskGroup):
|
||||
f"the return value ({coro!r}) is not a coroutine object"
|
||||
)
|
||||
|
||||
name = get_callable_name(func) if name is None else str(name)
|
||||
loop = asyncio.get_running_loop()
|
||||
if (
|
||||
(factory := loop.get_task_factory())
|
||||
and getattr(factory, "__code__", None) is _eager_task_factory_code
|
||||
and (closure := getattr(factory, "__closure__", None))
|
||||
):
|
||||
custom_task_constructor = closure[0].cell_contents
|
||||
task = custom_task_constructor(coro, loop=loop, name=name)
|
||||
else:
|
||||
task = create_task(coro, name=name)
|
||||
|
||||
# Make the spawned task inherit the task group's cancel scope
|
||||
_task_states[coro] = task_state = TaskState(
|
||||
_task_states[task] = TaskState(
|
||||
parent_id=parent_id, cancel_scope=self.cancel_scope
|
||||
)
|
||||
name = get_callable_name(func) if name is None else str(name)
|
||||
try:
|
||||
task = create_task(coro, name=name)
|
||||
finally:
|
||||
del _task_states[coro]
|
||||
|
||||
_task_states[task] = task_state
|
||||
self.cancel_scope._tasks.add(task)
|
||||
self._tasks.add(task)
|
||||
|
||||
if task.done():
|
||||
# This can happen with eager task factories
|
||||
task_done(task)
|
||||
else:
|
||||
task.add_done_callback(task_done)
|
||||
|
||||
task.add_done_callback(task_done)
|
||||
return task
|
||||
|
||||
def start_soon(
|
||||
@@ -2114,10 +2071,9 @@ class _SignalReceiver:
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> bool | None:
|
||||
) -> None:
|
||||
for sig in self._handled_signals:
|
||||
self._loop.remove_signal_handler(sig)
|
||||
return None
|
||||
|
||||
def __aiter__(self) -> _SignalReceiver:
|
||||
return self
|
||||
@@ -2446,7 +2402,7 @@ class AsyncIOBackend(AsyncBackend):
|
||||
return CapacityLimiter(total_tokens)
|
||||
|
||||
@classmethod
|
||||
async def run_sync_in_worker_thread(
|
||||
async def run_sync_in_worker_thread( # type: ignore[return]
|
||||
cls,
|
||||
func: Callable[[Unpack[PosArgsT]], T_Retval],
|
||||
args: tuple[Unpack[PosArgsT]],
|
||||
@@ -2468,7 +2424,7 @@ class AsyncIOBackend(AsyncBackend):
|
||||
|
||||
async with limiter or cls.current_default_thread_limiter():
|
||||
with CancelScope(shield=not abandon_on_cancel) as scope:
|
||||
future: asyncio.Future = asyncio.Future()
|
||||
future = asyncio.Future[T_Retval]()
|
||||
root_task = find_root_task()
|
||||
if not idle_workers:
|
||||
worker = WorkerThread(root_task, workers, idle_workers)
|
||||
|
@@ -132,8 +132,7 @@ class CancelScope(BaseCancelScope):
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> bool | None:
|
||||
# https://github.com/python-trio/trio-typing/pull/79
|
||||
) -> bool:
|
||||
return self.__original.__exit__(exc_type, exc_val, exc_tb)
|
||||
|
||||
def cancel(self) -> None:
|
||||
@@ -186,9 +185,10 @@ class TaskGroup(abc.TaskGroup):
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> bool | None:
|
||||
) -> bool:
|
||||
try:
|
||||
return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb)
|
||||
# trio.Nursery.__exit__ returns bool; .open_nursery has wrong type
|
||||
return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) # type: ignore[return-value]
|
||||
except BaseExceptionGroup as exc:
|
||||
if not exc.split(trio.Cancelled)[1]:
|
||||
raise trio.Cancelled._create() from exc
|
||||
|
@@ -21,6 +21,23 @@ class Selector:
|
||||
self._send, self._receive = socket.socketpair()
|
||||
self._send.setblocking(False)
|
||||
self._receive.setblocking(False)
|
||||
# This somewhat reduces the amount of memory wasted queueing up data
|
||||
# for wakeups. With these settings, maximum number of 1-byte sends
|
||||
# before getting BlockingIOError:
|
||||
# Linux 4.8: 6
|
||||
# macOS (darwin 15.5): 1
|
||||
# Windows 10: 525347
|
||||
# Windows you're weird. (And on Windows setting SNDBUF to 0 makes send
|
||||
# blocking, even on non-blocking sockets, so don't do that.)
|
||||
self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1)
|
||||
self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1)
|
||||
# On Windows this is a TCP socket so this might matter. On other
|
||||
# platforms this fails b/c AF_UNIX sockets aren't actually TCP.
|
||||
try:
|
||||
self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
self._selector.register(self._receive, EVENT_READ)
|
||||
self._closed = False
|
||||
|
||||
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from collections.abc import Generator
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
|
||||
if sys.version_info < (3, 11):
|
||||
from exceptiongroup import BaseExceptionGroup
|
||||
@@ -21,6 +23,41 @@ class BrokenWorkerProcess(Exception):
|
||||
"""
|
||||
|
||||
|
||||
class BrokenWorkerIntepreter(Exception):
|
||||
"""
|
||||
Raised by :meth:`~anyio.to_interpreter.run_sync` if an unexpected exception is
|
||||
raised in the subinterpreter.
|
||||
"""
|
||||
|
||||
def __init__(self, excinfo: Any):
|
||||
# This was adapted from concurrent.futures.interpreter.ExecutionFailed
|
||||
msg = excinfo.formatted
|
||||
if not msg:
|
||||
if excinfo.type and excinfo.msg:
|
||||
msg = f"{excinfo.type.__name__}: {excinfo.msg}"
|
||||
else:
|
||||
msg = excinfo.type.__name__ or excinfo.msg
|
||||
|
||||
super().__init__(msg)
|
||||
self.excinfo = excinfo
|
||||
|
||||
def __str__(self) -> str:
|
||||
try:
|
||||
formatted = self.excinfo.errdisplay
|
||||
except Exception:
|
||||
return super().__str__()
|
||||
else:
|
||||
return dedent(
|
||||
f"""
|
||||
{super().__str__()}
|
||||
|
||||
Uncaught in the interpreter:
|
||||
|
||||
{formatted}
|
||||
""".strip()
|
||||
)
|
||||
|
||||
|
||||
class BusyResourceError(Exception):
|
||||
"""
|
||||
Raised when two tasks are trying to read from or write to the same resource
|
||||
|
@@ -3,7 +3,13 @@ from __future__ import annotations
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
from collections.abc import AsyncIterator, Callable, Iterable, Iterator, Sequence
|
||||
from collections.abc import (
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Iterable,
|
||||
Iterator,
|
||||
Sequence,
|
||||
)
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
from os import PathLike
|
||||
@@ -220,11 +226,15 @@ class Path:
|
||||
Some methods may be unavailable or have limited functionality, based on the Python
|
||||
version:
|
||||
|
||||
* :meth:`~pathlib.Path.copy` (available on Python 3.14 or later)
|
||||
* :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later)
|
||||
* :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.move` (available on Python 3.14 or later)
|
||||
* :meth:`~pathlib.Path.move_into` (available on Python 3.14 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)
|
||||
@@ -396,6 +406,51 @@ class Path:
|
||||
def match(self, path_pattern: str) -> bool:
|
||||
return self._path.match(path_pattern)
|
||||
|
||||
if sys.version_info >= (3, 14):
|
||||
|
||||
async def copy(
|
||||
self,
|
||||
target: str | os.PathLike[str],
|
||||
*,
|
||||
follow_symlinks: bool = True,
|
||||
dirs_exist_ok: bool = False,
|
||||
preserve_metadata: bool = False,
|
||||
) -> Path:
|
||||
func = partial(
|
||||
self._path.copy,
|
||||
follow_symlinks=follow_symlinks,
|
||||
dirs_exist_ok=dirs_exist_ok,
|
||||
preserve_metadata=preserve_metadata,
|
||||
)
|
||||
return Path(await to_thread.run_sync(func, target))
|
||||
|
||||
async def copy_into(
|
||||
self,
|
||||
target_dir: str | os.PathLike[str],
|
||||
*,
|
||||
follow_symlinks: bool = True,
|
||||
dirs_exist_ok: bool = False,
|
||||
preserve_metadata: bool = False,
|
||||
) -> Path:
|
||||
func = partial(
|
||||
self._path.copy_into,
|
||||
follow_symlinks=follow_symlinks,
|
||||
dirs_exist_ok=dirs_exist_ok,
|
||||
preserve_metadata=preserve_metadata,
|
||||
)
|
||||
return Path(await to_thread.run_sync(func, target_dir))
|
||||
|
||||
async def move(self, target: str | os.PathLike[str]) -> Path:
|
||||
# Upstream does not handle anyio.Path properly as a PathLike
|
||||
target = pathlib.Path(target)
|
||||
return Path(await to_thread.run_sync(self._path.move, target))
|
||||
|
||||
async def move_into(
|
||||
self,
|
||||
target_dir: str | os.PathLike[str],
|
||||
) -> Path:
|
||||
return Path(await to_thread.run_sync(self._path.move_into, target_dir))
|
||||
|
||||
def is_relative_to(self, other: str | PathLike[str]) -> bool:
|
||||
try:
|
||||
self.relative_to(other)
|
||||
|
@@ -728,6 +728,5 @@ class ResourceGuard:
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> bool | None:
|
||||
) -> None:
|
||||
self._guarded = False
|
||||
return None
|
||||
|
@@ -88,7 +88,7 @@ class CancelScope:
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> bool | None:
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
@@ -35,7 +35,7 @@ _process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar(
|
||||
_default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter")
|
||||
|
||||
|
||||
async def run_sync(
|
||||
async def run_sync( # type: ignore[return]
|
||||
func: Callable[[Unpack[PosArgsT]], T_Retval],
|
||||
*args: Unpack[PosArgsT],
|
||||
cancellable: bool = False,
|
||||
|
Reference in New Issue
Block a user