prefect-client 3.1.5__py3-none-any.whl → 3.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefect/__init__.py +3 -0
- prefect/_experimental/__init__.py +0 -0
- prefect/_experimental/lineage.py +181 -0
- prefect/_internal/compatibility/async_dispatch.py +38 -9
- prefect/_internal/compatibility/migration.py +1 -1
- prefect/_internal/concurrency/api.py +52 -52
- prefect/_internal/concurrency/calls.py +59 -35
- prefect/_internal/concurrency/cancellation.py +34 -18
- prefect/_internal/concurrency/event_loop.py +7 -6
- prefect/_internal/concurrency/threads.py +41 -33
- prefect/_internal/concurrency/waiters.py +28 -21
- prefect/_internal/pydantic/v1_schema.py +2 -2
- prefect/_internal/pydantic/v2_schema.py +10 -9
- prefect/_internal/pydantic/v2_validated_func.py +15 -10
- prefect/_internal/retries.py +15 -6
- prefect/_internal/schemas/bases.py +11 -8
- prefect/_internal/schemas/validators.py +7 -5
- prefect/_version.py +3 -3
- prefect/automations.py +53 -47
- prefect/blocks/abstract.py +12 -10
- prefect/blocks/core.py +148 -19
- prefect/blocks/system.py +2 -1
- prefect/cache_policies.py +11 -11
- prefect/client/__init__.py +3 -1
- prefect/client/base.py +36 -37
- prefect/client/cloud.py +26 -19
- prefect/client/collections.py +2 -2
- prefect/client/orchestration.py +430 -273
- prefect/client/schemas/__init__.py +24 -0
- prefect/client/schemas/actions.py +128 -121
- prefect/client/schemas/filters.py +1 -1
- prefect/client/schemas/objects.py +114 -85
- prefect/client/schemas/responses.py +19 -20
- prefect/client/schemas/schedules.py +136 -93
- prefect/client/subscriptions.py +30 -15
- prefect/client/utilities.py +46 -36
- prefect/concurrency/asyncio.py +6 -9
- prefect/concurrency/sync.py +35 -5
- prefect/context.py +40 -32
- prefect/deployments/flow_runs.py +6 -8
- prefect/deployments/runner.py +14 -14
- prefect/deployments/steps/core.py +3 -1
- prefect/deployments/steps/pull.py +60 -12
- prefect/docker/__init__.py +1 -1
- prefect/events/clients.py +55 -4
- prefect/events/filters.py +1 -1
- prefect/events/related.py +2 -1
- prefect/events/schemas/events.py +26 -21
- prefect/events/utilities.py +3 -2
- prefect/events/worker.py +8 -0
- prefect/filesystems.py +3 -3
- prefect/flow_engine.py +87 -87
- prefect/flow_runs.py +7 -5
- prefect/flows.py +218 -176
- prefect/logging/configuration.py +1 -1
- prefect/logging/highlighters.py +1 -2
- prefect/logging/loggers.py +30 -20
- prefect/main.py +17 -24
- prefect/results.py +43 -22
- prefect/runner/runner.py +43 -21
- prefect/runner/server.py +30 -32
- prefect/runner/storage.py +3 -3
- prefect/runner/submit.py +3 -6
- prefect/runner/utils.py +6 -6
- prefect/runtime/flow_run.py +7 -0
- prefect/serializers.py +28 -24
- prefect/settings/constants.py +2 -2
- prefect/settings/legacy.py +1 -1
- prefect/settings/models/experiments.py +5 -0
- prefect/settings/models/server/events.py +10 -0
- prefect/task_engine.py +87 -26
- prefect/task_runners.py +2 -2
- prefect/task_worker.py +43 -25
- prefect/tasks.py +148 -142
- prefect/telemetry/bootstrap.py +15 -2
- prefect/telemetry/instrumentation.py +1 -1
- prefect/telemetry/processors.py +10 -7
- prefect/telemetry/run_telemetry.py +231 -0
- prefect/transactions.py +14 -14
- prefect/types/__init__.py +5 -5
- prefect/utilities/_engine.py +96 -0
- prefect/utilities/annotations.py +25 -18
- prefect/utilities/asyncutils.py +126 -140
- prefect/utilities/callables.py +87 -78
- prefect/utilities/collections.py +278 -117
- prefect/utilities/compat.py +13 -21
- prefect/utilities/context.py +6 -5
- prefect/utilities/dispatch.py +23 -12
- prefect/utilities/dockerutils.py +33 -32
- prefect/utilities/engine.py +126 -239
- prefect/utilities/filesystem.py +18 -15
- prefect/utilities/hashing.py +10 -11
- prefect/utilities/importtools.py +40 -27
- prefect/utilities/math.py +9 -5
- prefect/utilities/names.py +3 -3
- prefect/utilities/processutils.py +121 -57
- prefect/utilities/pydantic.py +41 -36
- prefect/utilities/render_swagger.py +22 -12
- prefect/utilities/schema_tools/__init__.py +2 -1
- prefect/utilities/schema_tools/hydration.py +50 -43
- prefect/utilities/schema_tools/validation.py +52 -42
- prefect/utilities/services.py +13 -12
- prefect/utilities/templating.py +45 -45
- prefect/utilities/text.py +2 -1
- prefect/utilities/timeout.py +4 -4
- prefect/utilities/urls.py +9 -4
- prefect/utilities/visualization.py +46 -24
- prefect/variables.py +136 -27
- prefect/workers/base.py +15 -8
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.7.dist-info}/METADATA +5 -2
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.7.dist-info}/RECORD +114 -110
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.7.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.7.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.7.dist-info}/top_level.txt +0 -0
@@ -12,18 +12,20 @@ import dataclasses
|
|
12
12
|
import inspect
|
13
13
|
import threading
|
14
14
|
import weakref
|
15
|
+
from collections.abc import Awaitable, Generator
|
15
16
|
from concurrent.futures._base import (
|
16
17
|
CANCELLED,
|
17
18
|
CANCELLED_AND_NOTIFIED,
|
18
19
|
FINISHED,
|
19
20
|
RUNNING,
|
20
21
|
)
|
21
|
-
from typing import
|
22
|
+
from typing import TYPE_CHECKING, Any, Callable, Generic, Optional, Union
|
22
23
|
|
23
|
-
from typing_extensions import ParamSpec
|
24
|
+
from typing_extensions import ParamSpec, Self, TypeAlias, TypeVar, TypeVarTuple
|
24
25
|
|
25
26
|
from prefect._internal.concurrency import logger
|
26
27
|
from prefect._internal.concurrency.cancellation import (
|
28
|
+
AsyncCancelScope,
|
27
29
|
CancelledError,
|
28
30
|
cancel_async_at,
|
29
31
|
cancel_sync_at,
|
@@ -31,9 +33,13 @@ from prefect._internal.concurrency.cancellation import (
|
|
31
33
|
)
|
32
34
|
from prefect._internal.concurrency.event_loop import get_running_loop
|
33
35
|
|
34
|
-
T = TypeVar("T")
|
36
|
+
T = TypeVar("T", infer_variance=True)
|
37
|
+
Ts = TypeVarTuple("Ts")
|
35
38
|
P = ParamSpec("P")
|
36
39
|
|
40
|
+
_SyncOrAsyncCallable: TypeAlias = Callable[P, Union[T, Awaitable[T]]]
|
41
|
+
|
42
|
+
|
37
43
|
# Tracks the current call being executed. Note that storing the `Call`
|
38
44
|
# object for an async call directly in the contextvar appears to create a
|
39
45
|
# memory leak, despite the fact that we `reset` when leaving the context
|
@@ -41,16 +47,16 @@ P = ParamSpec("P")
|
|
41
47
|
# we already have strong references to the `Call` objects in other places
|
42
48
|
# and b) this is used for performance optimizations where we have fallback
|
43
49
|
# behavior if this weakref is garbage collected. A fix for issue #10952.
|
44
|
-
current_call: contextvars.ContextVar["weakref.ref[Call]"] = ( # novm
|
50
|
+
current_call: contextvars.ContextVar["weakref.ref[Call[Any]]"] = ( # novm
|
45
51
|
contextvars.ContextVar("current_call")
|
46
52
|
)
|
47
53
|
|
48
54
|
# Create a strong reference to tasks to prevent destruction during execution errors
|
49
|
-
_ASYNC_TASK_REFS = set()
|
55
|
+
_ASYNC_TASK_REFS: set[asyncio.Task[None]] = set()
|
50
56
|
|
51
57
|
|
52
58
|
@contextlib.contextmanager
|
53
|
-
def set_current_call(call: "Call"):
|
59
|
+
def set_current_call(call: "Call[Any]") -> Generator[None, Any, None]:
|
54
60
|
token = current_call.set(weakref.ref(call))
|
55
61
|
try:
|
56
62
|
yield
|
@@ -58,7 +64,7 @@ def set_current_call(call: "Call"):
|
|
58
64
|
current_call.reset(token)
|
59
65
|
|
60
66
|
|
61
|
-
class Future(concurrent.futures.Future):
|
67
|
+
class Future(concurrent.futures.Future[T]):
|
62
68
|
"""
|
63
69
|
Extension of `concurrent.futures.Future` with support for cancellation of running
|
64
70
|
futures.
|
@@ -70,7 +76,7 @@ class Future(concurrent.futures.Future):
|
|
70
76
|
super().__init__()
|
71
77
|
self._cancel_scope = None
|
72
78
|
self._deadline = None
|
73
|
-
self._cancel_callbacks = []
|
79
|
+
self._cancel_callbacks: list[Callable[[], None]] = []
|
74
80
|
self._name = name
|
75
81
|
self._timed_out = False
|
76
82
|
|
@@ -79,7 +85,7 @@ class Future(concurrent.futures.Future):
|
|
79
85
|
return super().set_running_or_notify_cancel()
|
80
86
|
|
81
87
|
@contextlib.contextmanager
|
82
|
-
def enforce_async_deadline(self):
|
88
|
+
def enforce_async_deadline(self) -> Generator[AsyncCancelScope]:
|
83
89
|
with cancel_async_at(self._deadline, name=self._name) as self._cancel_scope:
|
84
90
|
for callback in self._cancel_callbacks:
|
85
91
|
self._cancel_scope.add_cancel_callback(callback)
|
@@ -92,7 +98,7 @@ class Future(concurrent.futures.Future):
|
|
92
98
|
self._cancel_scope.add_cancel_callback(callback)
|
93
99
|
yield self._cancel_scope
|
94
100
|
|
95
|
-
def add_cancel_callback(self, callback: Callable[[],
|
101
|
+
def add_cancel_callback(self, callback: Callable[[], Any]) -> None:
|
96
102
|
"""
|
97
103
|
Add a callback to be enforced on cancellation.
|
98
104
|
|
@@ -113,7 +119,7 @@ class Future(concurrent.futures.Future):
|
|
113
119
|
with self._condition:
|
114
120
|
return self._timed_out
|
115
121
|
|
116
|
-
def cancel(self):
|
122
|
+
def cancel(self) -> bool:
|
117
123
|
"""Cancel the future if possible.
|
118
124
|
|
119
125
|
Returns True if the future was cancelled, False otherwise. A future cannot be
|
@@ -147,7 +153,12 @@ class Future(concurrent.futures.Future):
|
|
147
153
|
self._invoke_callbacks()
|
148
154
|
return True
|
149
155
|
|
150
|
-
|
156
|
+
if TYPE_CHECKING:
|
157
|
+
|
158
|
+
def __get_result(self) -> T:
|
159
|
+
...
|
160
|
+
|
161
|
+
def result(self, timeout: Optional[float] = None) -> T:
|
151
162
|
"""Return the result of the call that the future represents.
|
152
163
|
|
153
164
|
Args:
|
@@ -186,7 +197,9 @@ class Future(concurrent.futures.Future):
|
|
186
197
|
# Break a reference cycle with the exception in self._exception
|
187
198
|
self = None
|
188
199
|
|
189
|
-
|
200
|
+
_done_callbacks: list[Callable[[Self], object]]
|
201
|
+
|
202
|
+
def _invoke_callbacks(self) -> None:
|
190
203
|
"""
|
191
204
|
Invoke our done callbacks and clean up cancel scopes and cancel
|
192
205
|
callbacks. Fixes a memory leak that hung on to Call objects,
|
@@ -206,7 +219,7 @@ class Future(concurrent.futures.Future):
|
|
206
219
|
|
207
220
|
self._cancel_callbacks = []
|
208
221
|
if self._cancel_scope:
|
209
|
-
self._cancel_scope
|
222
|
+
setattr(self._cancel_scope, "_callbacks", [])
|
210
223
|
self._cancel_scope = None
|
211
224
|
|
212
225
|
|
@@ -216,16 +229,21 @@ class Call(Generic[T]):
|
|
216
229
|
A deferred function call.
|
217
230
|
"""
|
218
231
|
|
219
|
-
future: Future
|
220
|
-
fn:
|
221
|
-
args:
|
222
|
-
kwargs:
|
232
|
+
future: Future[T]
|
233
|
+
fn: "_SyncOrAsyncCallable[..., T]"
|
234
|
+
args: tuple[Any, ...]
|
235
|
+
kwargs: dict[str, Any]
|
223
236
|
context: contextvars.Context
|
224
|
-
timeout: float
|
237
|
+
timeout: Optional[float]
|
225
238
|
runner: Optional["Portal"] = None
|
226
239
|
|
227
240
|
@classmethod
|
228
|
-
def new(
|
241
|
+
def new(
|
242
|
+
cls,
|
243
|
+
__fn: _SyncOrAsyncCallable[P, T],
|
244
|
+
*args: P.args,
|
245
|
+
**kwargs: P.kwargs,
|
246
|
+
) -> Self:
|
229
247
|
return cls(
|
230
248
|
future=Future(name=getattr(__fn, "__name__", str(__fn))),
|
231
249
|
fn=__fn,
|
@@ -255,7 +273,7 @@ class Call(Generic[T]):
|
|
255
273
|
|
256
274
|
self.runner = portal
|
257
275
|
|
258
|
-
def run(self) -> Optional[Awaitable[
|
276
|
+
def run(self) -> Optional[Awaitable[None]]:
|
259
277
|
"""
|
260
278
|
Execute the call and place the result on the future.
|
261
279
|
|
@@ -337,7 +355,7 @@ class Call(Generic[T]):
|
|
337
355
|
def cancel(self) -> bool:
|
338
356
|
return self.future.cancel()
|
339
357
|
|
340
|
-
def _run_sync(self):
|
358
|
+
def _run_sync(self) -> Optional[Awaitable[T]]:
|
341
359
|
cancel_scope = None
|
342
360
|
try:
|
343
361
|
with set_current_call(self):
|
@@ -348,8 +366,8 @@ class Call(Generic[T]):
|
|
348
366
|
# Forget this call's arguments in order to free up any memory
|
349
367
|
# that may be referenced by them; after a call has happened,
|
350
368
|
# there's no need to keep a reference to them
|
351
|
-
|
352
|
-
|
369
|
+
with contextlib.suppress(AttributeError):
|
370
|
+
del self.args, self.kwargs
|
353
371
|
|
354
372
|
# Return the coroutine for async execution
|
355
373
|
if inspect.isawaitable(result):
|
@@ -357,8 +375,10 @@ class Call(Generic[T]):
|
|
357
375
|
|
358
376
|
except CancelledError:
|
359
377
|
# Report cancellation
|
378
|
+
if TYPE_CHECKING:
|
379
|
+
assert cancel_scope is not None
|
360
380
|
if cancel_scope.timedout():
|
361
|
-
self.future
|
381
|
+
setattr(self.future, "_timed_out", True)
|
362
382
|
self.future.cancel()
|
363
383
|
elif cancel_scope.cancelled():
|
364
384
|
self.future.cancel()
|
@@ -374,8 +394,8 @@ class Call(Generic[T]):
|
|
374
394
|
self.future.set_result(result) # noqa: F821
|
375
395
|
logger.debug("Finished call %r", self) # noqa: F821
|
376
396
|
|
377
|
-
async def _run_async(self, coro):
|
378
|
-
cancel_scope = None
|
397
|
+
async def _run_async(self, coro: Awaitable[T]) -> None:
|
398
|
+
cancel_scope = result = None
|
379
399
|
try:
|
380
400
|
with set_current_call(self):
|
381
401
|
with self.future.enforce_async_deadline() as cancel_scope:
|
@@ -385,12 +405,14 @@ class Call(Generic[T]):
|
|
385
405
|
# Forget this call's arguments in order to free up any memory
|
386
406
|
# that may be referenced by them; after a call has happened,
|
387
407
|
# there's no need to keep a reference to them
|
388
|
-
|
389
|
-
|
408
|
+
with contextlib.suppress(AttributeError):
|
409
|
+
del self.args, self.kwargs
|
390
410
|
except CancelledError:
|
391
411
|
# Report cancellation
|
412
|
+
if TYPE_CHECKING:
|
413
|
+
assert cancel_scope is not None
|
392
414
|
if cancel_scope.timedout():
|
393
|
-
self.future
|
415
|
+
setattr(self.future, "_timed_out", True)
|
394
416
|
self.future.cancel()
|
395
417
|
elif cancel_scope.cancelled():
|
396
418
|
self.future.cancel()
|
@@ -403,10 +425,11 @@ class Call(Generic[T]):
|
|
403
425
|
# Prevent reference cycle in `exc`
|
404
426
|
del self
|
405
427
|
else:
|
428
|
+
# F821 ignored because Ruff gets confused about the del self above.
|
406
429
|
self.future.set_result(result) # noqa: F821
|
407
430
|
logger.debug("Finished async call %r", self) # noqa: F821
|
408
431
|
|
409
|
-
def __call__(self) -> T:
|
432
|
+
def __call__(self) -> Union[T, Awaitable[T]]:
|
410
433
|
"""
|
411
434
|
Execute the call and return its result.
|
412
435
|
|
@@ -417,7 +440,7 @@ class Call(Generic[T]):
|
|
417
440
|
# Return an awaitable if in an async context
|
418
441
|
if coro is not None:
|
419
442
|
|
420
|
-
async def run_and_return_result():
|
443
|
+
async def run_and_return_result() -> T:
|
421
444
|
await coro
|
422
445
|
return self.result()
|
423
446
|
|
@@ -428,8 +451,9 @@ class Call(Generic[T]):
|
|
428
451
|
def __repr__(self) -> str:
|
429
452
|
name = getattr(self.fn, "__name__", str(self.fn))
|
430
453
|
|
431
|
-
|
432
|
-
|
454
|
+
try:
|
455
|
+
args, kwargs = self.args, self.kwargs
|
456
|
+
except AttributeError:
|
433
457
|
call_args = "<dropped>"
|
434
458
|
else:
|
435
459
|
call_args = ", ".join(
|
@@ -450,7 +474,7 @@ class Portal(abc.ABC):
|
|
450
474
|
"""
|
451
475
|
|
452
476
|
@abc.abstractmethod
|
453
|
-
def submit(self, call: "Call") -> "Call":
|
477
|
+
def submit(self, call: "Call[T]") -> "Call[T]":
|
454
478
|
"""
|
455
479
|
Submit a call to execute elsewhere.
|
456
480
|
|
@@ -12,14 +12,15 @@ import signal
|
|
12
12
|
import sys
|
13
13
|
import threading
|
14
14
|
import time
|
15
|
-
from
|
15
|
+
from types import TracebackType
|
16
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, overload
|
16
17
|
|
17
18
|
import anyio
|
18
19
|
|
19
20
|
from prefect._internal.concurrency import logger
|
20
21
|
from prefect._internal.concurrency.event_loop import get_running_loop
|
21
22
|
|
22
|
-
_THREAD_SHIELDS:
|
23
|
+
_THREAD_SHIELDS: dict[threading.Thread, "ThreadShield"] = {}
|
23
24
|
_THREAD_SHIELDS_LOCK = threading.Lock()
|
24
25
|
|
25
26
|
|
@@ -42,14 +43,14 @@ class ThreadShield:
|
|
42
43
|
# Uses the Python implementation of the RLock instead of the C implementation
|
43
44
|
# because we need to inspect `_count` directly to check if the lock is active
|
44
45
|
# which is needed for delayed exception raising during alarms
|
45
|
-
self._lock = threading._RLock()
|
46
|
+
self._lock = threading._RLock() # type: ignore # yes, we want the private version
|
46
47
|
self._exception = None
|
47
48
|
self._owner = owner
|
48
49
|
|
49
50
|
def __enter__(self) -> None:
|
50
51
|
self._lock.__enter__()
|
51
52
|
|
52
|
-
def __exit__(self, *exc_info):
|
53
|
+
def __exit__(self, *exc_info: Any):
|
53
54
|
retval = self._lock.__exit__(*exc_info)
|
54
55
|
|
55
56
|
# Raise the exception if this is the last shield to exit in the owner thread
|
@@ -65,14 +66,14 @@ class ThreadShield:
|
|
65
66
|
|
66
67
|
return retval
|
67
68
|
|
68
|
-
def set_exception(self, exc:
|
69
|
+
def set_exception(self, exc: BaseException):
|
69
70
|
self._exception = exc
|
70
71
|
|
71
72
|
def active(self) -> bool:
|
72
73
|
"""
|
73
74
|
Returns true if the shield is active.
|
74
75
|
"""
|
75
|
-
return self._lock
|
76
|
+
return getattr(self._lock, "_count") > 0
|
76
77
|
|
77
78
|
|
78
79
|
class CancelledError(asyncio.CancelledError):
|
@@ -82,7 +83,7 @@ class CancelledError(asyncio.CancelledError):
|
|
82
83
|
pass
|
83
84
|
|
84
85
|
|
85
|
-
def _get_thread_shield(thread) -> ThreadShield:
|
86
|
+
def _get_thread_shield(thread: threading.Thread) -> ThreadShield:
|
86
87
|
with _THREAD_SHIELDS_LOCK:
|
87
88
|
if thread not in _THREAD_SHIELDS:
|
88
89
|
_THREAD_SHIELDS[thread] = ThreadShield(thread)
|
@@ -139,7 +140,7 @@ class CancelScope(abc.ABC):
|
|
139
140
|
self._end_time = None
|
140
141
|
self._timeout = timeout
|
141
142
|
self._lock = threading.Lock()
|
142
|
-
self._callbacks = []
|
143
|
+
self._callbacks: list[Callable[[], None]] = []
|
143
144
|
super().__init__()
|
144
145
|
|
145
146
|
def __enter__(self):
|
@@ -151,7 +152,9 @@ class CancelScope(abc.ABC):
|
|
151
152
|
logger.debug("%r entered", self)
|
152
153
|
return self
|
153
154
|
|
154
|
-
def __exit__(
|
155
|
+
def __exit__(
|
156
|
+
self, exc_type: type[BaseException], exc_val: Exception, exc_tb: TracebackType
|
157
|
+
) -> Optional[bool]:
|
155
158
|
with self._lock:
|
156
159
|
if not self._cancelled:
|
157
160
|
self._completed = True
|
@@ -195,7 +198,7 @@ class CancelScope(abc.ABC):
|
|
195
198
|
throw the cancelled error.
|
196
199
|
"""
|
197
200
|
with self._lock:
|
198
|
-
if not self.
|
201
|
+
if not self._started:
|
199
202
|
raise RuntimeError("Scope has not been entered.")
|
200
203
|
|
201
204
|
if self._completed:
|
@@ -247,7 +250,6 @@ class AsyncCancelScope(CancelScope):
|
|
247
250
|
self, name: Optional[str] = None, timeout: Optional[float] = None
|
248
251
|
) -> None:
|
249
252
|
super().__init__(name=name, timeout=timeout)
|
250
|
-
self.loop = None
|
251
253
|
|
252
254
|
def __enter__(self):
|
253
255
|
self.loop = asyncio.get_running_loop()
|
@@ -262,7 +264,9 @@ class AsyncCancelScope(CancelScope):
|
|
262
264
|
|
263
265
|
return self
|
264
266
|
|
265
|
-
def __exit__(
|
267
|
+
def __exit__(
|
268
|
+
self, exc_type: type[BaseException], exc_val: Exception, exc_tb: TracebackType
|
269
|
+
) -> bool:
|
266
270
|
if self._anyio_scope.cancel_called:
|
267
271
|
# Mark as cancelled
|
268
272
|
self.cancel(throw=False)
|
@@ -310,7 +314,7 @@ class NullCancelScope(CancelScope):
|
|
310
314
|
super().__init__(name, timeout)
|
311
315
|
self.reason = reason or "null cancel scope"
|
312
316
|
|
313
|
-
def cancel(self):
|
317
|
+
def cancel(self, throw: bool = True) -> bool:
|
314
318
|
logger.warning("%r cannot cancel %s.", self, self.reason)
|
315
319
|
return False
|
316
320
|
|
@@ -355,7 +359,7 @@ class AlarmCancelScope(CancelScope):
|
|
355
359
|
|
356
360
|
return self
|
357
361
|
|
358
|
-
def _sigalarm_to_error(self, *args):
|
362
|
+
def _sigalarm_to_error(self, *args: object) -> None:
|
359
363
|
logger.debug("%r captured alarm raising as cancelled error", self)
|
360
364
|
if self.cancel(throw=False):
|
361
365
|
shield = _get_thread_shield(threading.main_thread())
|
@@ -365,11 +369,13 @@ class AlarmCancelScope(CancelScope):
|
|
365
369
|
else:
|
366
370
|
raise CancelledError()
|
367
371
|
|
368
|
-
def __exit__(self, *_):
|
372
|
+
def __exit__(self, *_: Any) -> Optional[bool]:
|
369
373
|
retval = super().__exit__(*_)
|
370
374
|
|
371
375
|
if self.timeout is not None:
|
372
376
|
# Restore the previous timer
|
377
|
+
if TYPE_CHECKING:
|
378
|
+
assert self._previous_timer is not None
|
373
379
|
signal.setitimer(signal.ITIMER_REAL, *self._previous_timer)
|
374
380
|
|
375
381
|
# Restore the previous signal handler
|
@@ -417,7 +423,7 @@ class WatcherThreadCancelScope(CancelScope):
|
|
417
423
|
|
418
424
|
return self
|
419
425
|
|
420
|
-
def __exit__(self, *_):
|
426
|
+
def __exit__(self, *_: Any) -> Optional[bool]:
|
421
427
|
retval = super().__exit__(*_)
|
422
428
|
self._event.set()
|
423
429
|
if self._enforcer_thread:
|
@@ -466,7 +472,17 @@ class WatcherThreadCancelScope(CancelScope):
|
|
466
472
|
return True
|
467
473
|
|
468
474
|
|
469
|
-
|
475
|
+
@overload
|
476
|
+
def get_deadline(timeout: float) -> float:
|
477
|
+
...
|
478
|
+
|
479
|
+
|
480
|
+
@overload
|
481
|
+
def get_deadline(timeout: None) -> None:
|
482
|
+
...
|
483
|
+
|
484
|
+
|
485
|
+
def get_deadline(timeout: Optional[float]) -> Optional[float]:
|
470
486
|
"""
|
471
487
|
Compute an deadline given a timeout.
|
472
488
|
|
@@ -572,7 +588,7 @@ def cancel_sync_after(timeout: Optional[float], name: Optional[str] = None):
|
|
572
588
|
yield scope
|
573
589
|
|
574
590
|
|
575
|
-
def _send_exception_to_thread(thread: threading.Thread, exc_type:
|
591
|
+
def _send_exception_to_thread(thread: threading.Thread, exc_type: type[BaseException]):
|
576
592
|
"""
|
577
593
|
Raise an exception in a thread.
|
578
594
|
|
@@ -5,7 +5,8 @@ Thread-safe utilities for working with asynchronous event loops.
|
|
5
5
|
import asyncio
|
6
6
|
import concurrent.futures
|
7
7
|
import functools
|
8
|
-
from
|
8
|
+
from collections.abc import Coroutine
|
9
|
+
from typing import Any, Callable, Optional, TypeVar
|
9
10
|
|
10
11
|
from typing_extensions import ParamSpec
|
11
12
|
|
@@ -13,7 +14,7 @@ P = ParamSpec("P")
|
|
13
14
|
T = TypeVar("T")
|
14
15
|
|
15
16
|
|
16
|
-
def get_running_loop() -> Optional[asyncio.
|
17
|
+
def get_running_loop() -> Optional[asyncio.AbstractEventLoop]:
|
17
18
|
"""
|
18
19
|
Get the current running loop.
|
19
20
|
|
@@ -30,7 +31,7 @@ def call_soon_in_loop(
|
|
30
31
|
__fn: Callable[P, T],
|
31
32
|
*args: P.args,
|
32
33
|
**kwargs: P.kwargs,
|
33
|
-
) -> concurrent.futures.Future:
|
34
|
+
) -> concurrent.futures.Future[T]:
|
34
35
|
"""
|
35
36
|
Run a synchronous call in an event loop's thread from another thread.
|
36
37
|
|
@@ -38,7 +39,7 @@ def call_soon_in_loop(
|
|
38
39
|
|
39
40
|
Returns a future that can be used to retrieve the result of the call.
|
40
41
|
"""
|
41
|
-
future = concurrent.futures.Future()
|
42
|
+
future: concurrent.futures.Future[T] = concurrent.futures.Future()
|
42
43
|
|
43
44
|
@functools.wraps(__fn)
|
44
45
|
def wrapper() -> None:
|
@@ -62,8 +63,8 @@ def call_soon_in_loop(
|
|
62
63
|
|
63
64
|
|
64
65
|
async def run_coroutine_in_loop_from_async(
|
65
|
-
__loop: asyncio.AbstractEventLoop, __coro: Coroutine
|
66
|
-
) ->
|
66
|
+
__loop: asyncio.AbstractEventLoop, __coro: Coroutine[Any, Any, T]
|
67
|
+
) -> T:
|
67
68
|
"""
|
68
69
|
Run an asynchronous call in an event loop from an asynchronous context.
|
69
70
|
|