增加环绕侦察场景适配

This commit is contained in:
2026-01-08 15:44:38 +08:00
parent 3eba1f962b
commit 10c5bb5a8a
5441 changed files with 40219 additions and 379695 deletions

View File

@@ -1,5 +1,14 @@
from __future__ import annotations
__all__ = (
"BlockingPortal",
"BlockingPortalProvider",
"check_cancelled",
"run",
"run_sync",
"start_blocking_portal",
)
import sys
from collections.abc import Awaitable, Callable, Generator
from concurrent.futures import Future
@@ -9,6 +18,7 @@ from contextlib import (
contextmanager,
)
from dataclasses import dataclass, field
from functools import partial
from inspect import isawaitable
from threading import Lock, Thread, current_thread, get_ident
from types import TracebackType
@@ -21,7 +31,6 @@ from typing import (
)
from ._core._eventloop import (
get_async_backend,
get_cancelled_exc_class,
threadlocals,
)
@@ -30,7 +39,7 @@ from ._core._exceptions import NoEventLoopError
from ._core._synchronization import Event
from ._core._tasks import CancelScope, create_task_group
from .abc._tasks import TaskStatus
from .lowlevel import EventLoopToken
from .lowlevel import EventLoopToken, current_token
if sys.version_info >= (3, 11):
from typing import TypeVarTuple, Unpack
@@ -174,16 +183,18 @@ class _BlockingPortalTaskStatus(TaskStatus):
class BlockingPortal:
"""An object that lets external threads run code in an asynchronous event loop."""
"""
An object that lets external threads run code in an asynchronous event loop.
def __new__(cls) -> BlockingPortal:
return get_async_backend().create_blocking_portal()
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
current thread
"""
def __init__(self) -> None:
self._token = current_token()
self._event_loop_thread_id: int | None = get_ident()
self._stop_event = Event()
self._task_group = create_task_group()
self._cancelled_exc_class = get_cancelled_exc_class()
async def __aenter__(self) -> BlockingPortal:
await self._task_group.__aenter__()
@@ -234,25 +245,21 @@ class BlockingPortal:
future: Future[T_Retval],
) -> None:
def callback(f: Future[T_Retval]) -> None:
if f.cancelled() and self._event_loop_thread_id not in (
None,
get_ident(),
):
self.call(scope.cancel, "the future was cancelled")
if f.cancelled():
if self._event_loop_thread_id == get_ident():
scope.cancel("the future was cancelled")
elif self._event_loop_thread_id is not None:
self.call(scope.cancel, "the future was cancelled")
try:
retval_or_awaitable = func(*args, **kwargs)
if isawaitable(retval_or_awaitable):
with CancelScope() as scope:
if future.cancelled():
scope.cancel("the future was cancelled")
else:
future.add_done_callback(callback)
future.add_done_callback(callback)
retval = await retval_or_awaitable
else:
retval = retval_or_awaitable
except self._cancelled_exc_class:
except get_cancelled_exc_class():
future.cancel()
future.set_running_or_notify_cancel()
except BaseException as exc:
@@ -279,8 +286,6 @@ class BlockingPortal:
"""
Spawn a new task using the given callable.
Implementers must ensure that the future is resolved when the task finishes.
:param func: a callable
:param args: positional arguments to be passed to the callable
:param kwargs: keyword arguments to be passed to the callable
@@ -289,7 +294,15 @@ class BlockingPortal:
or the exception raised during its execution
"""
raise NotImplementedError
run_sync(
partial(self._task_group.start_soon, name=name),
self._call_func,
func,
args,
kwargs,
future,
token=self._token,
)
@overload
def call(