@ajksunkang-aios/kgraph-linux-x64 0.1.2 → 0.1.3
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.
- package/bin/kgraph-launcher +15 -3
- package/lib/kgraph/scripts/build-bundle.sh +17 -4
- package/lib/site-packages/outcome/__init__.py +20 -0
- package/lib/site-packages/outcome/_impl.py +239 -0
- package/lib/site-packages/outcome/_util.py +33 -0
- package/lib/site-packages/outcome/_version.py +7 -0
- package/lib/site-packages/outcome/py.typed +0 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sniffio/__init__.py +17 -0
- package/lib/site-packages/sniffio/_impl.py +95 -0
- package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
- package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
- package/lib/site-packages/sniffio/_version.py +3 -0
- package/lib/site-packages/sniffio/py.typed +0 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sortedcontainers/__init__.py +74 -0
- package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
- package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
- package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/trio/__init__.py +133 -0
- package/lib/site-packages/trio/__main__.py +3 -0
- package/lib/site-packages/trio/_abc.py +714 -0
- package/lib/site-packages/trio/_channel.py +610 -0
- package/lib/site-packages/trio/_core/__init__.py +94 -0
- package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
- package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
- package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
- package/lib/site-packages/trio/_core/_exceptions.py +169 -0
- package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
- package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
- package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
- package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
- package/lib/site-packages/trio/_core/_generated_run.py +269 -0
- package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
- package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
- package/lib/site-packages/trio/_core/_io_common.py +31 -0
- package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
- package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
- package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
- package/lib/site-packages/trio/_core/_ki.py +271 -0
- package/lib/site-packages/trio/_core/_local.py +104 -0
- package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
- package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
- package/lib/site-packages/trio/_core/_run.py +3148 -0
- package/lib/site-packages/trio/_core/_run_context.py +15 -0
- package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
- package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
- package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
- package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
- package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
- package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
- package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
- package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
- package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
- package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
- package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
- package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
- package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
- package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
- package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
- package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
- package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
- package/lib/site-packages/trio/_core/_traps.py +318 -0
- package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
- package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
- package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
- package/lib/site-packages/trio/_deprecate.py +171 -0
- package/lib/site-packages/trio/_dtls.py +1380 -0
- package/lib/site-packages/trio/_file_io.py +513 -0
- package/lib/site-packages/trio/_highlevel_generic.py +125 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
- package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
- package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
- package/lib/site-packages/trio/_highlevel_socket.py +423 -0
- package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
- package/lib/site-packages/trio/_path.py +289 -0
- package/lib/site-packages/trio/_repl.py +159 -0
- package/lib/site-packages/trio/_signals.py +185 -0
- package/lib/site-packages/trio/_socket.py +1326 -0
- package/lib/site-packages/trio/_ssl.py +964 -0
- package/lib/site-packages/trio/_subprocess.py +1178 -0
- package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
- package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
- package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
- package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
- package/lib/site-packages/trio/_sync.py +908 -0
- package/lib/site-packages/trio/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
- package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
- package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
- package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
- package/lib/site-packages/trio/_tests/test_abc.py +72 -0
- package/lib/site-packages/trio/_tests/test_channel.py +750 -0
- package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
- package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
- package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
- package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
- package/lib/site-packages/trio/_tests/test_exports.py +626 -0
- package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
- package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
- package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
- package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
- package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
- package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
- package/lib/site-packages/trio/_tests/test_path.py +279 -0
- package/lib/site-packages/trio/_tests/test_repl.py +428 -0
- package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
- package/lib/site-packages/trio/_tests/test_signals.py +186 -0
- package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
- package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
- package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
- package/lib/site-packages/trio/_tests/test_sync.py +735 -0
- package/lib/site-packages/trio/_tests/test_testing.py +682 -0
- package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
- package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
- package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
- package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
- package/lib/site-packages/trio/_tests/test_trio.py +8 -0
- package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
- package/lib/site-packages/trio/_tests/test_util.py +349 -0
- package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
- package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
- package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
- package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
- package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
- package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
- package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
- package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
- package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
- package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
- package/lib/site-packages/trio/_threads.py +610 -0
- package/lib/site-packages/trio/_timeouts.py +197 -0
- package/lib/site-packages/trio/_tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
- package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
- package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
- package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
- package/lib/site-packages/trio/_unix_pipes.py +197 -0
- package/lib/site-packages/trio/_util.py +385 -0
- package/lib/site-packages/trio/_version.py +3 -0
- package/lib/site-packages/trio/_wait_for_object.py +67 -0
- package/lib/site-packages/trio/_windows_pipes.py +144 -0
- package/lib/site-packages/trio/abc.py +23 -0
- package/lib/site-packages/trio/from_thread.py +13 -0
- package/lib/site-packages/trio/lowlevel.py +95 -0
- package/lib/site-packages/trio/py.typed +0 -0
- package/lib/site-packages/trio/socket.py +602 -0
- package/lib/site-packages/trio/testing/__init__.py +58 -0
- package/lib/site-packages/trio/testing/_check_streams.py +570 -0
- package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
- package/lib/site-packages/trio/testing/_fake_net.py +584 -0
- package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
- package/lib/site-packages/trio/testing/_network.py +36 -0
- package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
- package/lib/site-packages/trio/testing/_sequencer.py +87 -0
- package/lib/site-packages/trio/testing/_trio_test.py +50 -0
- package/lib/site-packages/trio/to_thread.py +4 -0
- package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
- package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
- package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
- package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
- package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
- package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# Little utilities we use internally
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import collections.abc
|
|
5
|
+
import inspect
|
|
6
|
+
import signal
|
|
7
|
+
from abc import ABCMeta
|
|
8
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
9
|
+
from typing import (
|
|
10
|
+
TYPE_CHECKING,
|
|
11
|
+
Any,
|
|
12
|
+
NoReturn,
|
|
13
|
+
TypeVar,
|
|
14
|
+
final as std_final,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from sniffio import thread_local as sniffio_loop
|
|
18
|
+
|
|
19
|
+
import trio
|
|
20
|
+
|
|
21
|
+
# Explicit "Any" is not allowed
|
|
22
|
+
CallT = TypeVar("CallT", bound=Callable[..., Any]) # type: ignore[explicit-any]
|
|
23
|
+
T = TypeVar("T")
|
|
24
|
+
RetT = TypeVar("RetT")
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
import sys
|
|
28
|
+
from types import AsyncGeneratorType, TracebackType
|
|
29
|
+
|
|
30
|
+
from typing_extensions import TypeVarTuple, Unpack
|
|
31
|
+
|
|
32
|
+
if sys.version_info < (3, 11):
|
|
33
|
+
from exceptiongroup import BaseExceptionGroup
|
|
34
|
+
|
|
35
|
+
PosArgsT = TypeVarTuple("PosArgsT")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# See: #461 as to why this is needed.
|
|
39
|
+
# The gist is that threading.main_thread() has the capability to lie to us
|
|
40
|
+
# if somebody else edits the threading ident cache to replace the main
|
|
41
|
+
# thread; causing threading.current_thread() to return a _DummyThread,
|
|
42
|
+
# causing the C-c check to fail, and so on.
|
|
43
|
+
# Trying to use signal out of the main thread will fail, so we can then
|
|
44
|
+
# reliably check if this is the main thread without relying on a
|
|
45
|
+
# potentially modified threading.
|
|
46
|
+
def is_main_thread() -> bool:
|
|
47
|
+
"""Attempt to reliably check if we are in the main thread."""
|
|
48
|
+
try:
|
|
49
|
+
signal.signal(signal.SIGINT, signal.getsignal(signal.SIGINT))
|
|
50
|
+
return True
|
|
51
|
+
except (TypeError, ValueError):
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
######
|
|
56
|
+
# Call the function and get the coroutine object, while giving helpful
|
|
57
|
+
# errors for common mistakes. Returns coroutine object.
|
|
58
|
+
######
|
|
59
|
+
def coroutine_or_error(
|
|
60
|
+
async_fn: Callable[[Unpack[PosArgsT]], Awaitable[RetT]],
|
|
61
|
+
*args: Unpack[PosArgsT],
|
|
62
|
+
) -> collections.abc.Coroutine[object, NoReturn, RetT]:
|
|
63
|
+
def _return_value_looks_like_wrong_library(value: object) -> bool:
|
|
64
|
+
# Returned by legacy @asyncio.coroutine functions, which includes
|
|
65
|
+
# a surprising proportion of asyncio builtins.
|
|
66
|
+
if isinstance(value, collections.abc.Generator):
|
|
67
|
+
return True
|
|
68
|
+
# The protocol for detecting an asyncio Future-like object
|
|
69
|
+
if getattr(value, "_asyncio_future_blocking", None) is not None:
|
|
70
|
+
return True
|
|
71
|
+
# This janky check catches tornado Futures and twisted Deferreds.
|
|
72
|
+
# By the time we're calling this function, we already know
|
|
73
|
+
# something has gone wrong, so a heuristic is pretty safe.
|
|
74
|
+
return value.__class__.__name__ in ("Future", "Deferred")
|
|
75
|
+
|
|
76
|
+
# Make sure a sync-fn-that-returns-coroutine still sees itself as being
|
|
77
|
+
# in trio context
|
|
78
|
+
prev_loop, sniffio_loop.name = sniffio_loop.name, "trio"
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
coro = async_fn(*args)
|
|
82
|
+
|
|
83
|
+
except TypeError:
|
|
84
|
+
# Give good error for: nursery.start_soon(trio.sleep(1))
|
|
85
|
+
if isinstance(async_fn, collections.abc.Coroutine):
|
|
86
|
+
# explicitly close coroutine to avoid RuntimeWarning
|
|
87
|
+
async_fn.close()
|
|
88
|
+
|
|
89
|
+
raise TypeError(
|
|
90
|
+
"Trio was expecting an async function, but instead it got "
|
|
91
|
+
f"a coroutine object {async_fn!r}\n"
|
|
92
|
+
"\n"
|
|
93
|
+
"Probably you did something like:\n"
|
|
94
|
+
"\n"
|
|
95
|
+
f" trio.run({async_fn.__name__}(...)) # incorrect!\n"
|
|
96
|
+
f" nursery.start_soon({async_fn.__name__}(...)) # incorrect!\n"
|
|
97
|
+
"\n"
|
|
98
|
+
"Instead, you want (notice the parentheses!):\n"
|
|
99
|
+
"\n"
|
|
100
|
+
f" trio.run({async_fn.__name__}, ...) # correct!\n"
|
|
101
|
+
f" nursery.start_soon({async_fn.__name__}, ...) # correct!",
|
|
102
|
+
) from None
|
|
103
|
+
|
|
104
|
+
# Give good error for: nursery.start_soon(future)
|
|
105
|
+
if _return_value_looks_like_wrong_library(async_fn):
|
|
106
|
+
raise TypeError(
|
|
107
|
+
"Trio was expecting an async function, but instead it got "
|
|
108
|
+
f"{async_fn!r} – are you trying to use a library written for "
|
|
109
|
+
"asyncio/twisted/tornado or similar? That won't work "
|
|
110
|
+
"without some sort of compatibility shim.",
|
|
111
|
+
) from None
|
|
112
|
+
|
|
113
|
+
raise
|
|
114
|
+
|
|
115
|
+
finally:
|
|
116
|
+
sniffio_loop.name = prev_loop
|
|
117
|
+
|
|
118
|
+
# We can't check iscoroutinefunction(async_fn), because that will fail
|
|
119
|
+
# for things like functools.partial objects wrapping an async
|
|
120
|
+
# function. So we have to just call it and then check whether the
|
|
121
|
+
# return value is a coroutine object.
|
|
122
|
+
# Note: will not be necessary on python>=3.8, see https://bugs.python.org/issue34890
|
|
123
|
+
# TODO: python3.7 support is now dropped, so the above can be addressed.
|
|
124
|
+
if not isinstance(coro, collections.abc.Coroutine):
|
|
125
|
+
# Give good error for: nursery.start_soon(func_returning_future)
|
|
126
|
+
if _return_value_looks_like_wrong_library(coro):
|
|
127
|
+
raise TypeError(
|
|
128
|
+
f"Trio got unexpected {coro!r} – are you trying to use a "
|
|
129
|
+
"library written for asyncio/twisted/tornado or similar? "
|
|
130
|
+
"That won't work without some sort of compatibility shim.",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if inspect.isasyncgen(coro):
|
|
134
|
+
raise TypeError(
|
|
135
|
+
"start_soon expected an async function but got an async "
|
|
136
|
+
f"generator {coro!r}",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Give good error for: nursery.start_soon(some_sync_fn)
|
|
140
|
+
raise TypeError(
|
|
141
|
+
"Trio expected an async function, but {!r} appears to be "
|
|
142
|
+
"synchronous".format(getattr(async_fn, "__qualname__", async_fn)),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return coro
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class ConflictDetector:
|
|
149
|
+
"""Detect when two tasks are about to perform operations that would
|
|
150
|
+
conflict.
|
|
151
|
+
|
|
152
|
+
Use as a synchronous context manager; if two tasks enter it at the same
|
|
153
|
+
time then the second one raises an error. You can use it when there are
|
|
154
|
+
two pieces of code that *would* collide and need a lock if they ever were
|
|
155
|
+
called at the same time, but that should never happen.
|
|
156
|
+
|
|
157
|
+
We use this in particular for things like, making sure that two different
|
|
158
|
+
tasks don't call sendall simultaneously on the same stream.
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(self, msg: str) -> None:
|
|
163
|
+
self._msg = msg
|
|
164
|
+
self._held = False
|
|
165
|
+
|
|
166
|
+
def __enter__(self) -> None:
|
|
167
|
+
if self._held:
|
|
168
|
+
raise trio.BusyResourceError(self._msg)
|
|
169
|
+
else:
|
|
170
|
+
self._held = True
|
|
171
|
+
|
|
172
|
+
def __exit__(
|
|
173
|
+
self,
|
|
174
|
+
exc_type: type[BaseException] | None,
|
|
175
|
+
exc_value: BaseException | None,
|
|
176
|
+
traceback: TracebackType | None,
|
|
177
|
+
) -> None:
|
|
178
|
+
self._held = False
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def async_wraps( # type: ignore[explicit-any]
|
|
182
|
+
cls: type[object],
|
|
183
|
+
wrapped_cls: type[object],
|
|
184
|
+
attr_name: str,
|
|
185
|
+
) -> Callable[[CallT], CallT]:
|
|
186
|
+
"""Similar to wraps, but for async wrappers of non-async functions."""
|
|
187
|
+
|
|
188
|
+
def decorator(func: CallT) -> CallT: # type: ignore[explicit-any]
|
|
189
|
+
func.__name__ = attr_name
|
|
190
|
+
func.__qualname__ = f"{cls.__qualname__}.{attr_name}"
|
|
191
|
+
|
|
192
|
+
func.__doc__ = f"Like :meth:`~{wrapped_cls.__module__}.{wrapped_cls.__qualname__}.{attr_name}`, but async."
|
|
193
|
+
|
|
194
|
+
return func
|
|
195
|
+
|
|
196
|
+
return decorator
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def fixup_module_metadata(
|
|
200
|
+
module_name: str,
|
|
201
|
+
namespace: collections.abc.Mapping[str, object],
|
|
202
|
+
) -> None:
|
|
203
|
+
seen_ids: set[int] = set()
|
|
204
|
+
|
|
205
|
+
def fix_one(qualname: str, name: str, obj: object) -> None:
|
|
206
|
+
# avoid infinite recursion (relevant when using
|
|
207
|
+
# typing.Generic, for example)
|
|
208
|
+
if id(obj) in seen_ids:
|
|
209
|
+
return
|
|
210
|
+
seen_ids.add(id(obj))
|
|
211
|
+
|
|
212
|
+
mod = getattr(obj, "__module__", None)
|
|
213
|
+
if mod is not None and mod.startswith("trio."):
|
|
214
|
+
obj.__module__ = module_name
|
|
215
|
+
# Modules, unlike everything else in Python, put fully-qualified
|
|
216
|
+
# names into their __name__ attribute. We check for "." to avoid
|
|
217
|
+
# rewriting these.
|
|
218
|
+
if hasattr(obj, "__name__") and "." not in obj.__name__:
|
|
219
|
+
obj.__name__ = name
|
|
220
|
+
if hasattr(obj, "__qualname__"):
|
|
221
|
+
obj.__qualname__ = qualname
|
|
222
|
+
if isinstance(obj, type):
|
|
223
|
+
for attr_name, attr_value in obj.__dict__.items():
|
|
224
|
+
fix_one(objname + "." + attr_name, attr_name, attr_value)
|
|
225
|
+
|
|
226
|
+
for objname, obj in namespace.items():
|
|
227
|
+
if not objname.startswith("_"): # ignore private attributes
|
|
228
|
+
fix_one(objname, objname, obj)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _init_final_cls(cls: type[object]) -> NoReturn:
|
|
232
|
+
"""Raises an exception when a final class is subclassed."""
|
|
233
|
+
raise TypeError(f"{cls.__module__}.{cls.__qualname__} does not support subclassing")
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _final_impl(decorated: type[T]) -> type[T]:
|
|
237
|
+
"""Decorator that enforces a class to be final (i.e., subclass not allowed).
|
|
238
|
+
|
|
239
|
+
If a class uses this metaclass like this::
|
|
240
|
+
|
|
241
|
+
@final
|
|
242
|
+
class SomeClass:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
The metaclass will ensure that no subclass can be created.
|
|
246
|
+
|
|
247
|
+
Raises
|
|
248
|
+
------
|
|
249
|
+
- TypeError if a subclass is created
|
|
250
|
+
"""
|
|
251
|
+
# Override the method blindly. We're always going to raise, so it doesn't
|
|
252
|
+
# matter what the original did (if anything).
|
|
253
|
+
decorated.__init_subclass__ = classmethod(_init_final_cls) # type: ignore[assignment]
|
|
254
|
+
# Apply the typing decorator, in 3.11+ it adds a __final__ marker attribute.
|
|
255
|
+
return std_final(decorated)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if TYPE_CHECKING:
|
|
259
|
+
from typing import final
|
|
260
|
+
else:
|
|
261
|
+
final = _final_impl
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@final # No subclassing of NoPublicConstructor itself.
|
|
265
|
+
class NoPublicConstructor(ABCMeta):
|
|
266
|
+
"""Metaclass that ensures a private constructor.
|
|
267
|
+
|
|
268
|
+
If a class uses this metaclass like this::
|
|
269
|
+
|
|
270
|
+
@final
|
|
271
|
+
class SomeClass(metaclass=NoPublicConstructor):
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
The metaclass will ensure that no instance can be initialized. This should always be
|
|
275
|
+
used with @final.
|
|
276
|
+
|
|
277
|
+
If you try to instantiate your class (SomeClass()), a TypeError will be thrown. Use
|
|
278
|
+
_create() instead in the class's implementation.
|
|
279
|
+
|
|
280
|
+
Raises
|
|
281
|
+
------
|
|
282
|
+
- TypeError if an instance is created.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
def __call__(cls, *args: object, **kwargs: object) -> None:
|
|
286
|
+
raise TypeError(
|
|
287
|
+
f"{cls.__module__}.{cls.__qualname__} has no public constructor",
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
def _create(cls: type[T], *args: object, **kwargs: object) -> T:
|
|
291
|
+
return super().__call__(*args, **kwargs) # type: ignore
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def name_asyncgen(agen: AsyncGeneratorType[object, NoReturn]) -> str:
|
|
295
|
+
"""Return the fully-qualified name of the async generator function
|
|
296
|
+
that produced the async generator iterator *agen*.
|
|
297
|
+
"""
|
|
298
|
+
if not hasattr(agen, "ag_code"): # pragma: no cover
|
|
299
|
+
return repr(agen)
|
|
300
|
+
try:
|
|
301
|
+
module = agen.ag_frame.f_globals["__name__"]
|
|
302
|
+
except (AttributeError, KeyError):
|
|
303
|
+
module = f"<{agen.ag_code.co_filename}>"
|
|
304
|
+
try:
|
|
305
|
+
qualname = agen.__qualname__
|
|
306
|
+
except AttributeError:
|
|
307
|
+
qualname = agen.ag_code.co_name
|
|
308
|
+
return f"{module}.{qualname}"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# work around a pyright error
|
|
312
|
+
if TYPE_CHECKING:
|
|
313
|
+
Fn = TypeVar("Fn", bound=Callable[..., object]) # type: ignore[explicit-any]
|
|
314
|
+
|
|
315
|
+
def wraps( # type: ignore[explicit-any]
|
|
316
|
+
wrapped: Callable[..., object],
|
|
317
|
+
assigned: Sequence[str] = ...,
|
|
318
|
+
updated: Sequence[str] = ...,
|
|
319
|
+
) -> Callable[[Fn], Fn]: ...
|
|
320
|
+
|
|
321
|
+
else:
|
|
322
|
+
from functools import wraps # noqa: F401 # this is re-exported
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def raise_saving_context(exc: BaseException) -> NoReturn:
|
|
326
|
+
"""This helper allows re-raising an exception without __context__ being set."""
|
|
327
|
+
# cause does not need special handling, we simply avoid using `raise .. from ..`
|
|
328
|
+
# __suppress_context__ also does not need handling, it's only set if modifying cause
|
|
329
|
+
__tracebackhide__ = True
|
|
330
|
+
context = exc.__context__
|
|
331
|
+
try:
|
|
332
|
+
raise exc
|
|
333
|
+
finally:
|
|
334
|
+
exc.__context__ = context
|
|
335
|
+
del exc, context
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class MultipleExceptionError(Exception):
|
|
339
|
+
"""Raised by raise_single_exception_from_group if encountering multiple
|
|
340
|
+
non-cancelled exceptions."""
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def raise_single_exception_from_group(
|
|
344
|
+
eg: BaseExceptionGroup[BaseException],
|
|
345
|
+
) -> NoReturn:
|
|
346
|
+
"""This function takes an exception group that is assumed to have at most
|
|
347
|
+
one non-cancelled exception, which it reraises as a standalone exception.
|
|
348
|
+
|
|
349
|
+
This exception may be an exceptiongroup itself, in which case it will not be unwrapped.
|
|
350
|
+
|
|
351
|
+
If a :exc:`KeyboardInterrupt` is encountered, a new KeyboardInterrupt is immediately
|
|
352
|
+
raised with the entire group as cause.
|
|
353
|
+
|
|
354
|
+
If the group only contains :exc:`Cancelled` it reraises the first one encountered.
|
|
355
|
+
|
|
356
|
+
It will retain context and cause of the contained exception, and entirely discard
|
|
357
|
+
the cause/context of the group(s).
|
|
358
|
+
|
|
359
|
+
If multiple non-cancelled exceptions are encountered, it raises
|
|
360
|
+
:exc:`AssertionError`.
|
|
361
|
+
"""
|
|
362
|
+
# immediately bail out if there's any KI or SystemExit
|
|
363
|
+
for e in eg.exceptions:
|
|
364
|
+
if isinstance(e, (KeyboardInterrupt, SystemExit)):
|
|
365
|
+
raise type(e)(*e.args) from eg
|
|
366
|
+
|
|
367
|
+
cancelled_exception: trio.Cancelled | None = None
|
|
368
|
+
noncancelled_exception: BaseException | None = None
|
|
369
|
+
|
|
370
|
+
for e in eg.exceptions:
|
|
371
|
+
if isinstance(e, trio.Cancelled):
|
|
372
|
+
if cancelled_exception is None:
|
|
373
|
+
cancelled_exception = e
|
|
374
|
+
elif noncancelled_exception is None:
|
|
375
|
+
noncancelled_exception = e
|
|
376
|
+
else:
|
|
377
|
+
raise MultipleExceptionError(
|
|
378
|
+
"Attempted to unwrap exceptiongroup with multiple non-cancelled exceptions. This is often caused by a bug in the caller."
|
|
379
|
+
) from eg
|
|
380
|
+
|
|
381
|
+
if noncancelled_exception is not None:
|
|
382
|
+
raise_saving_context(noncancelled_exception)
|
|
383
|
+
|
|
384
|
+
assert cancelled_exception is not None, "group can't be empty"
|
|
385
|
+
raise_saving_context(cancelled_exception)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
import trio
|
|
6
|
+
|
|
7
|
+
from ._core._windows_cffi import (
|
|
8
|
+
CData,
|
|
9
|
+
ErrorCodes,
|
|
10
|
+
_handle,
|
|
11
|
+
ffi,
|
|
12
|
+
handle_array,
|
|
13
|
+
kernel32,
|
|
14
|
+
raise_winerror,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def WaitForSingleObject(obj: int | CData) -> None:
|
|
19
|
+
"""Async and cancellable variant of WaitForSingleObject. Windows only.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
handle: A Win32 handle, as a Python integer.
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
OSError: If the handle is invalid, e.g. when it is already closed.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
# Allow ints or whatever we can convert to a win handle
|
|
29
|
+
handle = _handle(obj)
|
|
30
|
+
|
|
31
|
+
# Quick check; we might not even need to spawn a thread. The zero
|
|
32
|
+
# means a zero timeout; this call never blocks. We also exit here
|
|
33
|
+
# if the handle is already closed for some reason.
|
|
34
|
+
retcode = kernel32.WaitForSingleObject(handle, 0)
|
|
35
|
+
if retcode == ErrorCodes.WAIT_FAILED:
|
|
36
|
+
raise_winerror()
|
|
37
|
+
elif retcode != ErrorCodes.WAIT_TIMEOUT:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
# Wait for a thread that waits for two handles: the handle plus a handle
|
|
41
|
+
# that we can use to cancel the thread.
|
|
42
|
+
cancel_handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL)
|
|
43
|
+
try:
|
|
44
|
+
await trio.to_thread.run_sync(
|
|
45
|
+
WaitForMultipleObjects_sync,
|
|
46
|
+
handle,
|
|
47
|
+
cancel_handle,
|
|
48
|
+
abandon_on_cancel=True,
|
|
49
|
+
limiter=trio.CapacityLimiter(math.inf),
|
|
50
|
+
)
|
|
51
|
+
finally:
|
|
52
|
+
# Clean up our cancel handle. In case we get here because this task was
|
|
53
|
+
# cancelled, we also want to set the cancel_handle to stop the thread.
|
|
54
|
+
kernel32.SetEvent(cancel_handle)
|
|
55
|
+
kernel32.CloseHandle(cancel_handle)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def WaitForMultipleObjects_sync(*handles: int | CData) -> None:
|
|
59
|
+
"""Wait for any of the given Windows handles to be signaled."""
|
|
60
|
+
n = len(handles)
|
|
61
|
+
handle_arr = handle_array(n)
|
|
62
|
+
for i in range(n):
|
|
63
|
+
handle_arr[i] = handles[i]
|
|
64
|
+
timeout = 0xFFFFFFFF # INFINITE
|
|
65
|
+
retcode = kernel32.WaitForMultipleObjects(n, handle_arr, False, timeout) # blocking
|
|
66
|
+
if retcode == ErrorCodes.WAIT_FAILED:
|
|
67
|
+
raise_winerror()
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from . import _core
|
|
7
|
+
from ._abc import ReceiveStream, SendStream
|
|
8
|
+
from ._core._windows_cffi import _handle, kernel32, raise_winerror
|
|
9
|
+
from ._util import ConflictDetector, final
|
|
10
|
+
|
|
11
|
+
assert sys.platform == "win32" or not TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
# XX TODO: don't just make this up based on nothing.
|
|
14
|
+
DEFAULT_RECEIVE_SIZE = 65536
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# See the comments on _unix_pipes._FdHolder for discussion of why we set the
|
|
18
|
+
# handle to -1 when it's closed.
|
|
19
|
+
class _HandleHolder:
|
|
20
|
+
def __init__(self, handle: int) -> None:
|
|
21
|
+
self.handle = -1
|
|
22
|
+
if not isinstance(handle, int):
|
|
23
|
+
raise TypeError("handle must be an int")
|
|
24
|
+
self.handle = handle
|
|
25
|
+
_core.register_with_iocp(self.handle)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def closed(self) -> bool:
|
|
29
|
+
return self.handle == -1
|
|
30
|
+
|
|
31
|
+
def close(self) -> None:
|
|
32
|
+
if self.closed:
|
|
33
|
+
return
|
|
34
|
+
handle = self.handle
|
|
35
|
+
self.handle = -1
|
|
36
|
+
if not kernel32.CloseHandle(_handle(handle)):
|
|
37
|
+
raise_winerror()
|
|
38
|
+
|
|
39
|
+
def __del__(self) -> None:
|
|
40
|
+
self.close()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@final
|
|
44
|
+
class PipeSendStream(SendStream):
|
|
45
|
+
"""Represents a send stream over a Windows named pipe that has been
|
|
46
|
+
opened in OVERLAPPED mode.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, handle: int) -> None:
|
|
50
|
+
self._handle_holder = _HandleHolder(handle)
|
|
51
|
+
self._conflict_detector = ConflictDetector(
|
|
52
|
+
"another task is currently using this pipe",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async def send_all(self, data: bytes) -> None:
|
|
56
|
+
with self._conflict_detector:
|
|
57
|
+
if self._handle_holder.closed:
|
|
58
|
+
raise _core.ClosedResourceError("this pipe is already closed")
|
|
59
|
+
|
|
60
|
+
if not data:
|
|
61
|
+
await _core.checkpoint()
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
written = await _core.write_overlapped(self._handle_holder.handle, data)
|
|
66
|
+
except BrokenPipeError as ex:
|
|
67
|
+
raise _core.BrokenResourceError from ex
|
|
68
|
+
# By my reading of MSDN, this assert is guaranteed to pass so long
|
|
69
|
+
# as the pipe isn't in nonblocking mode, but... let's just
|
|
70
|
+
# double-check.
|
|
71
|
+
assert written == len(data)
|
|
72
|
+
|
|
73
|
+
async def wait_send_all_might_not_block(self) -> None:
|
|
74
|
+
with self._conflict_detector:
|
|
75
|
+
if self._handle_holder.closed:
|
|
76
|
+
raise _core.ClosedResourceError("This pipe is already closed")
|
|
77
|
+
|
|
78
|
+
# not implemented yet, and probably not needed
|
|
79
|
+
await _core.checkpoint()
|
|
80
|
+
|
|
81
|
+
def close(self) -> None:
|
|
82
|
+
self._handle_holder.close()
|
|
83
|
+
|
|
84
|
+
async def aclose(self) -> None:
|
|
85
|
+
self.close()
|
|
86
|
+
await _core.checkpoint()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@final
|
|
90
|
+
class PipeReceiveStream(ReceiveStream):
|
|
91
|
+
"""Represents a receive stream over an os.pipe object."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, handle: int) -> None:
|
|
94
|
+
self._handle_holder = _HandleHolder(handle)
|
|
95
|
+
self._conflict_detector = ConflictDetector(
|
|
96
|
+
"another task is currently using this pipe",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
async def receive_some(self, max_bytes: int | None = None) -> bytes:
|
|
100
|
+
with self._conflict_detector:
|
|
101
|
+
if self._handle_holder.closed:
|
|
102
|
+
raise _core.ClosedResourceError("this pipe is already closed")
|
|
103
|
+
|
|
104
|
+
if max_bytes is None:
|
|
105
|
+
max_bytes = DEFAULT_RECEIVE_SIZE
|
|
106
|
+
else:
|
|
107
|
+
if not isinstance(max_bytes, int):
|
|
108
|
+
raise TypeError("max_bytes must be integer >= 1")
|
|
109
|
+
if max_bytes < 1:
|
|
110
|
+
raise ValueError("max_bytes must be integer >= 1")
|
|
111
|
+
|
|
112
|
+
buffer = bytearray(max_bytes)
|
|
113
|
+
try:
|
|
114
|
+
size = await _core.readinto_overlapped(
|
|
115
|
+
self._handle_holder.handle,
|
|
116
|
+
buffer,
|
|
117
|
+
)
|
|
118
|
+
except BrokenPipeError:
|
|
119
|
+
if self._handle_holder.closed:
|
|
120
|
+
raise _core.ClosedResourceError(
|
|
121
|
+
"another task closed this pipe",
|
|
122
|
+
) from None
|
|
123
|
+
|
|
124
|
+
# Windows raises BrokenPipeError on one end of a pipe
|
|
125
|
+
# whenever the other end closes, regardless of direction.
|
|
126
|
+
# Convert this to the Unix behavior of returning EOF to the
|
|
127
|
+
# reader when the writer closes.
|
|
128
|
+
#
|
|
129
|
+
# And since we're not raising an exception, we have to
|
|
130
|
+
# checkpoint. But readinto_overlapped did raise an exception,
|
|
131
|
+
# so it might not have checkpointed for us. So we have to
|
|
132
|
+
# checkpoint manually.
|
|
133
|
+
await _core.checkpoint()
|
|
134
|
+
return b""
|
|
135
|
+
else:
|
|
136
|
+
del buffer[size:]
|
|
137
|
+
return buffer
|
|
138
|
+
|
|
139
|
+
def close(self) -> None:
|
|
140
|
+
self._handle_holder.close()
|
|
141
|
+
|
|
142
|
+
async def aclose(self) -> None:
|
|
143
|
+
self.close()
|
|
144
|
+
await _core.checkpoint()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# This is a public namespace, so we don't want to expose any non-underscored
|
|
2
|
+
# attributes that aren't actually part of our public API. But it's very
|
|
3
|
+
# annoying to carefully always use underscored names for module-level
|
|
4
|
+
# temporaries, imports, etc. when implementing the module. So we put the
|
|
5
|
+
# implementation in an underscored module, and then re-export the public parts
|
|
6
|
+
# here.
|
|
7
|
+
|
|
8
|
+
# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)
|
|
9
|
+
from ._abc import (
|
|
10
|
+
AsyncResource as AsyncResource,
|
|
11
|
+
Channel as Channel,
|
|
12
|
+
Clock as Clock,
|
|
13
|
+
HalfCloseableStream as HalfCloseableStream,
|
|
14
|
+
HostnameResolver as HostnameResolver,
|
|
15
|
+
Instrument as Instrument,
|
|
16
|
+
Listener as Listener,
|
|
17
|
+
ReceiveChannel as ReceiveChannel,
|
|
18
|
+
ReceiveStream as ReceiveStream,
|
|
19
|
+
SendChannel as SendChannel,
|
|
20
|
+
SendStream as SendStream,
|
|
21
|
+
SocketFactory as SocketFactory,
|
|
22
|
+
Stream as Stream,
|
|
23
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This namespace represents special functions that can call back into Trio from
|
|
3
|
+
an external thread by means of a Trio Token present in Thread Local Storage
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ._threads import (
|
|
7
|
+
from_thread_check_cancelled as check_cancelled,
|
|
8
|
+
from_thread_run as run,
|
|
9
|
+
from_thread_run_sync as run_sync,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# need to use __all__ for pyright --verifytypes to see re-exports when renaming them
|
|
13
|
+
__all__ = ["check_cancelled", "run", "run_sync"]
|