@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,243 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
import warnings
|
|
6
|
+
import weakref
|
|
7
|
+
from typing import TYPE_CHECKING, NoReturn, TypeVar
|
|
8
|
+
|
|
9
|
+
import attrs
|
|
10
|
+
|
|
11
|
+
from .. import _core
|
|
12
|
+
from .._util import name_asyncgen
|
|
13
|
+
from . import _run
|
|
14
|
+
|
|
15
|
+
# Used to log exceptions in async generator finalizers
|
|
16
|
+
ASYNCGEN_LOGGER = logging.getLogger("trio.async_generator_errors")
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from types import AsyncGeneratorType
|
|
21
|
+
|
|
22
|
+
from typing_extensions import ParamSpec
|
|
23
|
+
|
|
24
|
+
_P = ParamSpec("_P")
|
|
25
|
+
|
|
26
|
+
_WEAK_ASYNC_GEN_SET = weakref.WeakSet[AsyncGeneratorType[object, NoReturn]]
|
|
27
|
+
_ASYNC_GEN_SET = set[AsyncGeneratorType[object, NoReturn]]
|
|
28
|
+
else:
|
|
29
|
+
_WEAK_ASYNC_GEN_SET = weakref.WeakSet
|
|
30
|
+
_ASYNC_GEN_SET = set
|
|
31
|
+
|
|
32
|
+
_R = TypeVar("_R")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@_core.disable_ki_protection
|
|
36
|
+
def _call_without_ki_protection(
|
|
37
|
+
f: Callable[_P, _R],
|
|
38
|
+
/,
|
|
39
|
+
*args: _P.args,
|
|
40
|
+
**kwargs: _P.kwargs,
|
|
41
|
+
) -> _R:
|
|
42
|
+
return f(*args, **kwargs)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@attrs.define(eq=False)
|
|
46
|
+
class AsyncGenerators:
|
|
47
|
+
# Async generators are added to this set when first iterated. Any
|
|
48
|
+
# left after the main task exits will be closed before trio.run()
|
|
49
|
+
# returns. During most of the run, this is a WeakSet so GC works.
|
|
50
|
+
# During shutdown, when we're finalizing all the remaining
|
|
51
|
+
# asyncgens after the system nursery has been closed, it's a
|
|
52
|
+
# regular set so we don't have to deal with GC firing at
|
|
53
|
+
# unexpected times.
|
|
54
|
+
alive: _WEAK_ASYNC_GEN_SET | _ASYNC_GEN_SET = attrs.Factory(_WEAK_ASYNC_GEN_SET)
|
|
55
|
+
# The ids of foreign async generators are added to this set when first
|
|
56
|
+
# iterated. Usually it is not safe to refer to ids like this, but because
|
|
57
|
+
# we're using a finalizer we can ensure ids in this set do not outlive
|
|
58
|
+
# their async generator.
|
|
59
|
+
foreign: set[int] = attrs.Factory(set)
|
|
60
|
+
|
|
61
|
+
# This collects async generators that get garbage collected during
|
|
62
|
+
# the one-tick window between the system nursery closing and the
|
|
63
|
+
# init task starting end-of-run asyncgen finalization.
|
|
64
|
+
trailing_needs_finalize: _ASYNC_GEN_SET = attrs.Factory(_ASYNC_GEN_SET)
|
|
65
|
+
|
|
66
|
+
prev_hooks: sys._asyncgen_hooks = attrs.field(init=False)
|
|
67
|
+
|
|
68
|
+
def install_hooks(self, runner: _run.Runner) -> None:
|
|
69
|
+
def firstiter(agen: AsyncGeneratorType[object, NoReturn]) -> None:
|
|
70
|
+
if hasattr(_run.GLOBAL_RUN_CONTEXT, "task"):
|
|
71
|
+
self.alive.add(agen)
|
|
72
|
+
else:
|
|
73
|
+
# An async generator first iterated outside of a Trio
|
|
74
|
+
# task doesn't belong to Trio. Probably we're in guest
|
|
75
|
+
# mode and the async generator belongs to our host.
|
|
76
|
+
# A strong set of ids is one of the only good places to
|
|
77
|
+
# remember this fact, at least until
|
|
78
|
+
# https://github.com/python/cpython/issues/85093 is implemented.
|
|
79
|
+
self.foreign.add(id(agen))
|
|
80
|
+
if self.prev_hooks.firstiter is not None:
|
|
81
|
+
self.prev_hooks.firstiter(agen)
|
|
82
|
+
|
|
83
|
+
def finalize_in_trio_context(
|
|
84
|
+
agen: AsyncGeneratorType[object, NoReturn],
|
|
85
|
+
agen_name: str,
|
|
86
|
+
) -> None:
|
|
87
|
+
try:
|
|
88
|
+
runner.spawn_system_task(
|
|
89
|
+
self._finalize_one,
|
|
90
|
+
agen,
|
|
91
|
+
agen_name,
|
|
92
|
+
name=f"close asyncgen {agen_name} (abandoned)",
|
|
93
|
+
)
|
|
94
|
+
except RuntimeError:
|
|
95
|
+
# There is a one-tick window where the system nursery
|
|
96
|
+
# is closed but the init task hasn't yet made
|
|
97
|
+
# self.asyncgens a strong set to disable GC. We seem to
|
|
98
|
+
# have hit it.
|
|
99
|
+
self.trailing_needs_finalize.add(agen)
|
|
100
|
+
|
|
101
|
+
@_core.enable_ki_protection
|
|
102
|
+
def finalizer(agen: AsyncGeneratorType[object, NoReturn]) -> None:
|
|
103
|
+
try:
|
|
104
|
+
self.foreign.remove(id(agen))
|
|
105
|
+
except KeyError:
|
|
106
|
+
is_ours = True
|
|
107
|
+
else:
|
|
108
|
+
is_ours = False
|
|
109
|
+
|
|
110
|
+
agen_name = name_asyncgen(agen)
|
|
111
|
+
if is_ours:
|
|
112
|
+
runner.entry_queue.run_sync_soon(
|
|
113
|
+
finalize_in_trio_context,
|
|
114
|
+
agen,
|
|
115
|
+
agen_name,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Do this last, because it might raise an exception
|
|
119
|
+
# depending on the user's warnings filter. (That
|
|
120
|
+
# exception will be printed to the terminal and
|
|
121
|
+
# ignored, since we're running in GC context.)
|
|
122
|
+
warnings.warn(
|
|
123
|
+
f"Async generator {agen_name!r} was garbage collected before it "
|
|
124
|
+
"had been exhausted. Surround its use in 'async with "
|
|
125
|
+
"aclosing(...):' to ensure that it gets cleaned up as soon as "
|
|
126
|
+
"you're done using it.",
|
|
127
|
+
ResourceWarning,
|
|
128
|
+
stacklevel=2,
|
|
129
|
+
source=agen,
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
# Not ours -> forward to the host loop's async generator finalizer
|
|
133
|
+
finalizer = self.prev_hooks.finalizer
|
|
134
|
+
if finalizer is not None:
|
|
135
|
+
_call_without_ki_protection(finalizer, agen)
|
|
136
|
+
else:
|
|
137
|
+
# Host has no finalizer. Reimplement the default
|
|
138
|
+
# Python behavior with no hooks installed: throw in
|
|
139
|
+
# GeneratorExit, step once, raise RuntimeError if
|
|
140
|
+
# it doesn't exit.
|
|
141
|
+
closer = agen.aclose()
|
|
142
|
+
try:
|
|
143
|
+
# If the next thing is a yield, this will raise RuntimeError
|
|
144
|
+
# which we allow to propagate
|
|
145
|
+
_call_without_ki_protection(closer.send, None)
|
|
146
|
+
except StopIteration:
|
|
147
|
+
pass
|
|
148
|
+
else:
|
|
149
|
+
# If the next thing is an await, we get here. Give a nicer
|
|
150
|
+
# error than the default "async generator ignored GeneratorExit"
|
|
151
|
+
raise RuntimeError(
|
|
152
|
+
f"Non-Trio async generator {agen_name!r} awaited something "
|
|
153
|
+
"during finalization; install a finalization hook to "
|
|
154
|
+
"support this, or wrap it in 'async with aclosing(...):'",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
self.prev_hooks = sys.get_asyncgen_hooks()
|
|
158
|
+
sys.set_asyncgen_hooks(firstiter=firstiter, finalizer=finalizer) # type: ignore[arg-type] # Finalizer doesn't use AsyncGeneratorType
|
|
159
|
+
|
|
160
|
+
async def finalize_remaining(self, runner: _run.Runner) -> None:
|
|
161
|
+
# This is called from init after shutting down the system nursery.
|
|
162
|
+
# The only tasks running at this point are init and
|
|
163
|
+
# the run_sync_soon task, and since the system nursery is closed,
|
|
164
|
+
# there's no way for user code to spawn more.
|
|
165
|
+
assert _core.current_task() is runner.init_task
|
|
166
|
+
assert len(runner.tasks) == 2
|
|
167
|
+
|
|
168
|
+
# To make async generator finalization easier to reason
|
|
169
|
+
# about, we'll shut down asyncgen garbage collection by turning
|
|
170
|
+
# the alive WeakSet into a regular set.
|
|
171
|
+
self.alive = set(self.alive)
|
|
172
|
+
|
|
173
|
+
# Process all pending run_sync_soon callbacks, in case one of
|
|
174
|
+
# them was an asyncgen finalizer that snuck in under the wire.
|
|
175
|
+
runner.entry_queue.run_sync_soon(runner.reschedule, runner.init_task)
|
|
176
|
+
await _core.wait_task_rescheduled(
|
|
177
|
+
lambda _: _core.Abort.FAILED, # pragma: no cover
|
|
178
|
+
)
|
|
179
|
+
self.alive.update(self.trailing_needs_finalize)
|
|
180
|
+
self.trailing_needs_finalize.clear()
|
|
181
|
+
|
|
182
|
+
# None of the still-living tasks use async generators, so
|
|
183
|
+
# every async generator must be suspended at a yield point --
|
|
184
|
+
# there's no one to be doing the iteration. That's good,
|
|
185
|
+
# because aclose() only works on an asyncgen that's suspended
|
|
186
|
+
# at a yield point. (If it's suspended at an event loop trap,
|
|
187
|
+
# because someone is in the middle of iterating it, then you
|
|
188
|
+
# get a RuntimeError on 3.8+, and a nasty surprise on earlier
|
|
189
|
+
# versions due to https://bugs.python.org/issue32526.)
|
|
190
|
+
#
|
|
191
|
+
# However, once we start aclose() of one async generator, it
|
|
192
|
+
# might start fetching the next value from another, thus
|
|
193
|
+
# preventing us from closing that other (at least until
|
|
194
|
+
# aclose() of the first one is complete). This constraint
|
|
195
|
+
# effectively requires us to finalize the remaining asyncgens
|
|
196
|
+
# in arbitrary order, rather than doing all of them at the
|
|
197
|
+
# same time. On 3.8+ we could defer any generator with
|
|
198
|
+
# ag_running=True to a later batch, but that only catches
|
|
199
|
+
# the case where our aclose() starts after the user's
|
|
200
|
+
# asend()/etc. If our aclose() starts first, then the
|
|
201
|
+
# user's asend()/etc will raise RuntimeError, since they're
|
|
202
|
+
# probably not checking ag_running.
|
|
203
|
+
#
|
|
204
|
+
# It might be possible to allow some parallelized cleanup if
|
|
205
|
+
# we can determine that a certain set of asyncgens have no
|
|
206
|
+
# interdependencies, using gc.get_referents() and such.
|
|
207
|
+
# But just doing one at a time will typically work well enough
|
|
208
|
+
# (since each aclose() executes in a cancelled scope) and
|
|
209
|
+
# is much easier to reason about.
|
|
210
|
+
|
|
211
|
+
# It's possible that that cleanup code will itself create
|
|
212
|
+
# more async generators, so we iterate repeatedly until
|
|
213
|
+
# all are gone.
|
|
214
|
+
while self.alive:
|
|
215
|
+
batch = self.alive
|
|
216
|
+
self.alive = _ASYNC_GEN_SET()
|
|
217
|
+
for agen in batch:
|
|
218
|
+
await self._finalize_one(agen, name_asyncgen(agen))
|
|
219
|
+
|
|
220
|
+
def close(self) -> None:
|
|
221
|
+
sys.set_asyncgen_hooks(*self.prev_hooks)
|
|
222
|
+
|
|
223
|
+
async def _finalize_one(
|
|
224
|
+
self,
|
|
225
|
+
agen: AsyncGeneratorType[object, NoReturn],
|
|
226
|
+
name: object,
|
|
227
|
+
) -> None:
|
|
228
|
+
try:
|
|
229
|
+
# This shield ensures that finalize_asyncgen never exits
|
|
230
|
+
# with an exception, not even a Cancelled. The inside
|
|
231
|
+
# is cancelled so there's no deadlock risk.
|
|
232
|
+
with _core.CancelScope(shield=True) as cancel_scope:
|
|
233
|
+
cancel_scope.cancel(
|
|
234
|
+
reason="disallow async work when closing async generators during trio shutdown"
|
|
235
|
+
)
|
|
236
|
+
await agen.aclose()
|
|
237
|
+
except BaseException:
|
|
238
|
+
ASYNCGEN_LOGGER.exception(
|
|
239
|
+
"Exception ignored during finalization of async generator %r -- "
|
|
240
|
+
"surround your use of the generator in 'async with aclosing(...):' "
|
|
241
|
+
"to raise exceptions like this in the context where they're generated",
|
|
242
|
+
name,
|
|
243
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from types import TracebackType
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# this is used for collapsing single-exception ExceptionGroups when using
|
|
7
|
+
# `strict_exception_groups=False`. Once that is retired this function can
|
|
8
|
+
# be removed as well.
|
|
9
|
+
def concat_tb(
|
|
10
|
+
head: TracebackType | None,
|
|
11
|
+
tail: TracebackType | None,
|
|
12
|
+
) -> TracebackType | None:
|
|
13
|
+
# We have to use an iterative algorithm here, because in the worst case
|
|
14
|
+
# this might be a RecursionError stack that is by definition too deep to
|
|
15
|
+
# process by recursion!
|
|
16
|
+
head_tbs = []
|
|
17
|
+
pointer = head
|
|
18
|
+
while pointer is not None:
|
|
19
|
+
head_tbs.append(pointer)
|
|
20
|
+
pointer = pointer.tb_next
|
|
21
|
+
current_head = tail
|
|
22
|
+
for head_tb in reversed(head_tbs):
|
|
23
|
+
current_head = TracebackType(
|
|
24
|
+
current_head, head_tb.tb_frame, head_tb.tb_lasti, head_tb.tb_lineno
|
|
25
|
+
)
|
|
26
|
+
return current_head
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from collections import deque
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import TYPE_CHECKING, NoReturn
|
|
7
|
+
|
|
8
|
+
import attrs
|
|
9
|
+
|
|
10
|
+
from .. import _core
|
|
11
|
+
from .._util import NoPublicConstructor, final
|
|
12
|
+
from ._wakeup_socketpair import WakeupSocketpair
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing_extensions import TypeVarTuple, Unpack
|
|
16
|
+
|
|
17
|
+
PosArgsT = TypeVarTuple("PosArgsT")
|
|
18
|
+
|
|
19
|
+
Function = Callable[..., object] # type: ignore[explicit-any]
|
|
20
|
+
Job = tuple[Function, tuple[object, ...]]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@attrs.define
|
|
24
|
+
class EntryQueue:
|
|
25
|
+
# This used to use a queue.Queue. but that was broken, because Queues are
|
|
26
|
+
# implemented in Python, and not reentrant -- so it was thread-safe, but
|
|
27
|
+
# not signal-safe. deque is implemented in C, so each operation is atomic
|
|
28
|
+
# WRT threads (and this is guaranteed in the docs), AND each operation is
|
|
29
|
+
# atomic WRT signal delivery (signal handlers can run on either side, but
|
|
30
|
+
# not *during* a deque operation). dict makes similar guarantees - and
|
|
31
|
+
# it's even ordered!
|
|
32
|
+
queue: deque[Job] = attrs.Factory(deque)
|
|
33
|
+
idempotent_queue: dict[Job, None] = attrs.Factory(dict)
|
|
34
|
+
|
|
35
|
+
wakeup: WakeupSocketpair = attrs.Factory(WakeupSocketpair)
|
|
36
|
+
done: bool = False
|
|
37
|
+
# Must be a reentrant lock, because it's acquired from signal handlers.
|
|
38
|
+
# RLock is signal-safe as of cpython 3.2. NB that this does mean that the
|
|
39
|
+
# lock is effectively *disabled* when we enter from signal context. The
|
|
40
|
+
# way we use the lock this is OK though, because when
|
|
41
|
+
# run_sync_soon is called from a signal it's atomic WRT the
|
|
42
|
+
# main thread -- it just might happen at some inconvenient place. But if
|
|
43
|
+
# you look at the one place where the main thread holds the lock, it's
|
|
44
|
+
# just to make 1 assignment, so that's atomic WRT a signal anyway.
|
|
45
|
+
lock: threading.RLock = attrs.Factory(threading.RLock)
|
|
46
|
+
|
|
47
|
+
async def task(self) -> None:
|
|
48
|
+
assert _core.currently_ki_protected()
|
|
49
|
+
# RLock has two implementations: a signal-safe version in _thread, and
|
|
50
|
+
# and signal-UNsafe version in threading. We need the signal safe
|
|
51
|
+
# version. Python 3.2 and later should always use this anyway, but,
|
|
52
|
+
# since the symptoms if this goes wrong are just "weird rare
|
|
53
|
+
# deadlocks", then let's make a little check.
|
|
54
|
+
# See:
|
|
55
|
+
# https://bugs.python.org/issue13697#msg237140
|
|
56
|
+
assert self.lock.__class__.__module__ == "_thread"
|
|
57
|
+
|
|
58
|
+
def run_cb(job: Job) -> None:
|
|
59
|
+
# We run this with KI protection enabled; it's the callback's
|
|
60
|
+
# job to disable it if it wants it disabled. Exceptions are
|
|
61
|
+
# treated like system task exceptions (i.e., converted into
|
|
62
|
+
# TrioInternalError and cause everything to shut down).
|
|
63
|
+
sync_fn, args = job
|
|
64
|
+
try:
|
|
65
|
+
sync_fn(*args)
|
|
66
|
+
except BaseException as exc:
|
|
67
|
+
|
|
68
|
+
async def kill_everything( # noqa: RUF029 # await not used
|
|
69
|
+
exc: BaseException,
|
|
70
|
+
) -> NoReturn:
|
|
71
|
+
raise exc
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
_core.spawn_system_task(kill_everything, exc)
|
|
75
|
+
except RuntimeError:
|
|
76
|
+
# We're quite late in the shutdown process and the
|
|
77
|
+
# system nursery is already closed.
|
|
78
|
+
# TODO(2020-06): this is a gross hack and should
|
|
79
|
+
# be fixed soon when we address #1607.
|
|
80
|
+
parent_nursery = _core.current_task().parent_nursery
|
|
81
|
+
if parent_nursery is None:
|
|
82
|
+
raise AssertionError(
|
|
83
|
+
"Internal error: `parent_nursery` should never be `None`",
|
|
84
|
+
) from exc # pragma: no cover
|
|
85
|
+
parent_nursery.start_soon(kill_everything, exc)
|
|
86
|
+
|
|
87
|
+
# This has to be carefully written to be safe in the face of new items
|
|
88
|
+
# being queued while we iterate, and to do a bounded amount of work on
|
|
89
|
+
# each pass:
|
|
90
|
+
def run_all_bounded() -> None:
|
|
91
|
+
for _ in range(len(self.queue)):
|
|
92
|
+
run_cb(self.queue.popleft())
|
|
93
|
+
for job in list(self.idempotent_queue):
|
|
94
|
+
del self.idempotent_queue[job]
|
|
95
|
+
run_cb(job)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
while True:
|
|
99
|
+
run_all_bounded()
|
|
100
|
+
if not self.queue and not self.idempotent_queue:
|
|
101
|
+
await self.wakeup.wait_woken()
|
|
102
|
+
else:
|
|
103
|
+
await _core.checkpoint()
|
|
104
|
+
except _core.Cancelled:
|
|
105
|
+
# Keep the work done with this lock held as minimal as possible,
|
|
106
|
+
# because it doesn't protect us against concurrent signal delivery
|
|
107
|
+
# (see the comment above). Notice that this code would still be
|
|
108
|
+
# correct if written like:
|
|
109
|
+
# self.done = True
|
|
110
|
+
# with self.lock:
|
|
111
|
+
# pass
|
|
112
|
+
# because all we want is to force run_sync_soon
|
|
113
|
+
# to either be completely before or completely after the write to
|
|
114
|
+
# done. That's why we don't need the lock to protect
|
|
115
|
+
# against signal handlers.
|
|
116
|
+
with self.lock:
|
|
117
|
+
self.done = True
|
|
118
|
+
# No more jobs will be submitted, so just clear out any residual
|
|
119
|
+
# ones:
|
|
120
|
+
run_all_bounded()
|
|
121
|
+
assert not self.queue
|
|
122
|
+
assert not self.idempotent_queue
|
|
123
|
+
|
|
124
|
+
def close(self) -> None:
|
|
125
|
+
self.wakeup.close()
|
|
126
|
+
|
|
127
|
+
def size(self) -> int:
|
|
128
|
+
return len(self.queue) + len(self.idempotent_queue)
|
|
129
|
+
|
|
130
|
+
def run_sync_soon(
|
|
131
|
+
self,
|
|
132
|
+
sync_fn: Callable[[Unpack[PosArgsT]], object],
|
|
133
|
+
*args: Unpack[PosArgsT],
|
|
134
|
+
idempotent: bool = False,
|
|
135
|
+
) -> None:
|
|
136
|
+
with self.lock:
|
|
137
|
+
if self.done:
|
|
138
|
+
raise _core.RunFinishedError("run() has exited")
|
|
139
|
+
# We have to hold the lock all the way through here, because
|
|
140
|
+
# otherwise the main thread might exit *while* we're doing these
|
|
141
|
+
# calls, and then our queue item might not be processed, or the
|
|
142
|
+
# wakeup call might trigger an OSError b/c the IO manager has
|
|
143
|
+
# already been shut down.
|
|
144
|
+
if idempotent:
|
|
145
|
+
self.idempotent_queue[sync_fn, args] = None
|
|
146
|
+
else:
|
|
147
|
+
self.queue.append((sync_fn, args))
|
|
148
|
+
self.wakeup.wakeup_thread_and_signal_safe()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@final
|
|
152
|
+
@attrs.define(eq=False)
|
|
153
|
+
class TrioToken(metaclass=NoPublicConstructor):
|
|
154
|
+
"""An opaque object representing a single call to :func:`trio.run`.
|
|
155
|
+
|
|
156
|
+
It has no public constructor; instead, see :func:`current_trio_token`.
|
|
157
|
+
|
|
158
|
+
This object has two uses:
|
|
159
|
+
|
|
160
|
+
1. It lets you re-enter the Trio run loop from external threads or signal
|
|
161
|
+
handlers. This is the low-level primitive that :func:`trio.to_thread`
|
|
162
|
+
and `trio.from_thread` use to communicate with worker threads, that
|
|
163
|
+
`trio.open_signal_receiver` uses to receive notifications about
|
|
164
|
+
signals, and so forth.
|
|
165
|
+
|
|
166
|
+
2. Each call to :func:`trio.run` has exactly one associated
|
|
167
|
+
:class:`TrioToken` object, so you can use it to identify a particular
|
|
168
|
+
call.
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
_reentry_queue: EntryQueue
|
|
173
|
+
|
|
174
|
+
def run_sync_soon(
|
|
175
|
+
self,
|
|
176
|
+
sync_fn: Callable[[Unpack[PosArgsT]], object],
|
|
177
|
+
*args: Unpack[PosArgsT],
|
|
178
|
+
idempotent: bool = False,
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Schedule a call to ``sync_fn(*args)`` to occur in the context of a
|
|
181
|
+
Trio task.
|
|
182
|
+
|
|
183
|
+
This is safe to call from the main thread, from other threads, and
|
|
184
|
+
from signal handlers. This is the fundamental primitive used to
|
|
185
|
+
re-enter the Trio run loop from outside of it.
|
|
186
|
+
|
|
187
|
+
The call will happen "soon", but there's no guarantee about exactly
|
|
188
|
+
when, and no mechanism provided for finding out when it's happened.
|
|
189
|
+
If you need this, you'll have to build your own.
|
|
190
|
+
|
|
191
|
+
The call is effectively run as part of a system task (see
|
|
192
|
+
:func:`~trio.lowlevel.spawn_system_task`). In particular this means
|
|
193
|
+
that:
|
|
194
|
+
|
|
195
|
+
* :exc:`KeyboardInterrupt` protection is *enabled* by default; if
|
|
196
|
+
you want ``sync_fn`` to be interruptible by control-C, then you
|
|
197
|
+
need to use :func:`~trio.lowlevel.disable_ki_protection`
|
|
198
|
+
explicitly.
|
|
199
|
+
|
|
200
|
+
* If ``sync_fn`` raises an exception, then it's converted into a
|
|
201
|
+
:exc:`~trio.TrioInternalError` and *all* tasks are cancelled. You
|
|
202
|
+
should be careful that ``sync_fn`` doesn't crash.
|
|
203
|
+
|
|
204
|
+
All calls with ``idempotent=False`` are processed in strict
|
|
205
|
+
first-in first-out order.
|
|
206
|
+
|
|
207
|
+
If ``idempotent=True``, then ``sync_fn`` and ``args`` must be
|
|
208
|
+
hashable, and Trio will make a best-effort attempt to discard any
|
|
209
|
+
call submission which is equal to an already-pending call. Trio
|
|
210
|
+
will process these in first-in first-out order.
|
|
211
|
+
|
|
212
|
+
Any ordering guarantees apply separately to ``idempotent=False``
|
|
213
|
+
and ``idempotent=True`` calls; there's no rule for how calls in the
|
|
214
|
+
different categories are ordered with respect to each other.
|
|
215
|
+
|
|
216
|
+
:raises trio.RunFinishedError:
|
|
217
|
+
if the associated call to :func:`trio.run`
|
|
218
|
+
has already exited. (Any call that *doesn't* raise this error
|
|
219
|
+
is guaranteed to be fully processed before :func:`trio.run`
|
|
220
|
+
exits.)
|
|
221
|
+
|
|
222
|
+
"""
|
|
223
|
+
self._reentry_queue.run_sync_soon(sync_fn, *args, idempotent=idempotent)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import partial
|
|
4
|
+
from typing import TYPE_CHECKING, Literal, TypeAlias
|
|
5
|
+
|
|
6
|
+
import attrs
|
|
7
|
+
|
|
8
|
+
from trio._util import NoPublicConstructor, final
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
|
|
13
|
+
from typing_extensions import Self
|
|
14
|
+
|
|
15
|
+
CancelReasonLiteral: TypeAlias = Literal[
|
|
16
|
+
"KeyboardInterrupt",
|
|
17
|
+
"deadline",
|
|
18
|
+
"explicit",
|
|
19
|
+
"nursery",
|
|
20
|
+
"shutdown",
|
|
21
|
+
"unknown",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TrioInternalError(Exception):
|
|
26
|
+
"""Raised by :func:`run` if we encounter a bug in Trio, or (possibly) a
|
|
27
|
+
misuse of one of the low-level :mod:`trio.lowlevel` APIs.
|
|
28
|
+
|
|
29
|
+
This should never happen! If you get this error, please file a bug.
|
|
30
|
+
|
|
31
|
+
Unfortunately, if you get this error it also means that all bets are off –
|
|
32
|
+
Trio doesn't know what is going on and its normal invariants may be void.
|
|
33
|
+
(For example, we might have "lost track" of a task. Or lost track of all
|
|
34
|
+
tasks.) Again, though, this shouldn't happen.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RunFinishedError(RuntimeError):
|
|
40
|
+
"""Raised by `trio.from_thread.run` and similar functions if the
|
|
41
|
+
corresponding call to :func:`trio.run` has already finished.
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class WouldBlock(Exception):
|
|
47
|
+
"""Raised by ``X_nowait`` functions if ``X`` would block."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@final
|
|
51
|
+
@attrs.define(eq=False, kw_only=True)
|
|
52
|
+
class Cancelled(BaseException, metaclass=NoPublicConstructor):
|
|
53
|
+
"""Raised by blocking calls if the surrounding scope has been cancelled.
|
|
54
|
+
|
|
55
|
+
You should let this exception propagate, to be caught by the relevant
|
|
56
|
+
cancel scope. To remind you of this, it inherits from :exc:`BaseException`
|
|
57
|
+
instead of :exc:`Exception`, just like :exc:`KeyboardInterrupt` and
|
|
58
|
+
:exc:`SystemExit` do. This means that if you write something like::
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
...
|
|
62
|
+
except Exception:
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
then this *won't* catch a :exc:`Cancelled` exception.
|
|
66
|
+
|
|
67
|
+
You cannot raise :exc:`Cancelled` yourself. Attempting to do so
|
|
68
|
+
will produce a :exc:`TypeError`. Use :meth:`cancel_scope.cancel()
|
|
69
|
+
<trio.CancelScope.cancel>` instead.
|
|
70
|
+
|
|
71
|
+
.. note::
|
|
72
|
+
|
|
73
|
+
In the US it's also common to see this word spelled "canceled", with
|
|
74
|
+
only one "l". This is a `recent
|
|
75
|
+
<https://books.google.com/ngrams/graph?content=canceled%2Ccancelled&year_start=1800&year_end=2000&corpus=5&smoothing=3&direct_url=t1%3B%2Ccanceled%3B%2Cc0%3B.t1%3B%2Ccancelled%3B%2Cc0>`__
|
|
76
|
+
and `US-specific
|
|
77
|
+
<https://books.google.com/ngrams/graph?content=canceled%2Ccancelled&year_start=1800&year_end=2000&corpus=18&smoothing=3&share=&direct_url=t1%3B%2Ccanceled%3B%2Cc0%3B.t1%3B%2Ccancelled%3B%2Cc0>`__
|
|
78
|
+
innovation, and even in the US both forms are still commonly used. So
|
|
79
|
+
for consistency with the rest of the world and with "cancellation"
|
|
80
|
+
(which always has two "l"s), Trio uses the two "l" spelling
|
|
81
|
+
everywhere.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
source: CancelReasonLiteral = "unknown"
|
|
86
|
+
# repr(Task), so as to avoid gc troubles from holding a reference
|
|
87
|
+
source_task: str | None = None
|
|
88
|
+
reason: str | None = None
|
|
89
|
+
|
|
90
|
+
def __str__(self) -> str:
|
|
91
|
+
return (
|
|
92
|
+
f"cancelled due to {self.source}"
|
|
93
|
+
+ ("" if self.reason is None else f" with reason {self.reason!r}")
|
|
94
|
+
+ ("" if self.source_task is None else f" from task {self.source_task}")
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def __reduce__(self) -> tuple[Callable[[], Cancelled], tuple[()]]:
|
|
98
|
+
# The `__reduce__` tuple does not support directly passing kwargs, and the
|
|
99
|
+
# kwargs are required so we can't use the third item for adding to __dict__,
|
|
100
|
+
# so we use partial.
|
|
101
|
+
return (
|
|
102
|
+
partial(
|
|
103
|
+
Cancelled._create,
|
|
104
|
+
source=self.source,
|
|
105
|
+
source_task=self.source_task,
|
|
106
|
+
reason=self.reason,
|
|
107
|
+
),
|
|
108
|
+
(),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if TYPE_CHECKING:
|
|
112
|
+
# for type checking on internal code
|
|
113
|
+
@classmethod
|
|
114
|
+
def _create(
|
|
115
|
+
cls,
|
|
116
|
+
*,
|
|
117
|
+
source: CancelReasonLiteral = "unknown",
|
|
118
|
+
source_task: str | None = None,
|
|
119
|
+
reason: str | None = None,
|
|
120
|
+
) -> Self: ...
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class BusyResourceError(Exception):
|
|
124
|
+
"""Raised when a task attempts to use a resource that some other task is
|
|
125
|
+
already using, and this would lead to bugs and nonsense.
|
|
126
|
+
|
|
127
|
+
For example, if two tasks try to send data through the same socket at the
|
|
128
|
+
same time, Trio will raise :class:`BusyResourceError` instead of letting
|
|
129
|
+
the data get scrambled.
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class ClosedResourceError(Exception):
|
|
135
|
+
"""Raised when attempting to use a resource after it has been closed.
|
|
136
|
+
|
|
137
|
+
Note that "closed" here means that *your* code closed the resource,
|
|
138
|
+
generally by calling a method with a name like ``close`` or ``aclose``, or
|
|
139
|
+
by exiting a context manager. If a problem arises elsewhere – for example,
|
|
140
|
+
because of a network failure, or because a remote peer closed their end of
|
|
141
|
+
a connection – then that should be indicated by a different exception
|
|
142
|
+
class, like :exc:`BrokenResourceError` or an :exc:`OSError` subclass.
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class BrokenResourceError(Exception):
|
|
148
|
+
"""Raised when an attempt to use a resource fails due to external
|
|
149
|
+
circumstances.
|
|
150
|
+
|
|
151
|
+
For example, you might get this if you try to send data on a stream where
|
|
152
|
+
the remote side has already closed the connection.
|
|
153
|
+
|
|
154
|
+
You *don't* get this error if *you* closed the resource – in that case you
|
|
155
|
+
get :class:`ClosedResourceError`.
|
|
156
|
+
|
|
157
|
+
This exception's ``__cause__`` attribute will often contain more
|
|
158
|
+
information about the underlying error.
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class EndOfChannel(Exception):
|
|
164
|
+
"""Raised when trying to receive from a :class:`trio.abc.ReceiveChannel`
|
|
165
|
+
that has no more data to receive.
|
|
166
|
+
|
|
167
|
+
This is analogous to an "end-of-file" condition, but for channels.
|
|
168
|
+
|
|
169
|
+
"""
|