@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,610 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from collections import OrderedDict, deque
|
|
5
|
+
from collections.abc import AsyncGenerator, Callable # noqa: TC003 # Needed for Sphinx
|
|
6
|
+
from contextlib import AbstractAsyncContextManager, asynccontextmanager
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from math import inf
|
|
9
|
+
from typing import (
|
|
10
|
+
TYPE_CHECKING,
|
|
11
|
+
Generic,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
import attrs
|
|
15
|
+
from outcome import Error, Value
|
|
16
|
+
|
|
17
|
+
import trio
|
|
18
|
+
|
|
19
|
+
from ._abc import ReceiveChannel, ReceiveType, SendChannel, SendType, T
|
|
20
|
+
from ._core import Abort, BrokenResourceError, RaiseCancelT, Task, enable_ki_protection
|
|
21
|
+
from ._util import (
|
|
22
|
+
MultipleExceptionError,
|
|
23
|
+
NoPublicConstructor,
|
|
24
|
+
final,
|
|
25
|
+
raise_single_exception_from_group,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if sys.version_info < (3, 11):
|
|
29
|
+
from exceptiongroup import BaseExceptionGroup
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from types import TracebackType
|
|
33
|
+
|
|
34
|
+
from typing_extensions import ParamSpec, Self
|
|
35
|
+
|
|
36
|
+
P = ParamSpec("P")
|
|
37
|
+
elif "sphinx.ext.autodoc" in sys.modules:
|
|
38
|
+
# P needs to exist for Sphinx to parse the type hints successfully.
|
|
39
|
+
try:
|
|
40
|
+
from typing_extensions import ParamSpec
|
|
41
|
+
except ImportError:
|
|
42
|
+
P = ... # This is valid in Callable, though not correct
|
|
43
|
+
else:
|
|
44
|
+
P = ParamSpec("P")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# written as a class so you can say open_memory_channel[int](5)
|
|
48
|
+
@final
|
|
49
|
+
class open_memory_channel(tuple["MemorySendChannel[T]", "MemoryReceiveChannel[T]"]):
|
|
50
|
+
"""Open a channel for passing objects between tasks within a process.
|
|
51
|
+
|
|
52
|
+
Memory channels are lightweight, cheap to allocate, and entirely
|
|
53
|
+
in-memory. They don't involve any operating-system resources, or any kind
|
|
54
|
+
of serialization. They just pass Python objects directly between tasks
|
|
55
|
+
(with a possible stop in an internal buffer along the way).
|
|
56
|
+
|
|
57
|
+
Channel objects can be closed by calling `~trio.abc.AsyncResource.aclose`
|
|
58
|
+
or using ``async with``. They are *not* automatically closed when garbage
|
|
59
|
+
collected. Closing memory channels isn't mandatory, but it is generally a
|
|
60
|
+
good idea, because it helps avoid situations where tasks get stuck waiting
|
|
61
|
+
on a channel when there's no-one on the other side. See
|
|
62
|
+
:ref:`channel-shutdown` for details.
|
|
63
|
+
|
|
64
|
+
Memory channel operations are all atomic with respect to
|
|
65
|
+
cancellation, either `~trio.abc.ReceiveChannel.receive` will
|
|
66
|
+
successfully return an object, or it will raise :exc:`Cancelled`
|
|
67
|
+
while leaving the channel unchanged.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
max_buffer_size (int or math.inf): The maximum number of items that can
|
|
71
|
+
be buffered in the channel before :meth:`~trio.abc.SendChannel.send`
|
|
72
|
+
blocks. Choosing a sensible value here is important to ensure that
|
|
73
|
+
backpressure is communicated promptly and avoid unnecessary latency;
|
|
74
|
+
see :ref:`channel-buffering` for more details. If in doubt, use 0.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A pair ``(send_channel, receive_channel)``. If you have
|
|
78
|
+
trouble remembering which order these go in, remember: data
|
|
79
|
+
flows from left → right.
|
|
80
|
+
|
|
81
|
+
In addition to the standard channel methods, all memory channel objects
|
|
82
|
+
provide a ``statistics()`` method, which returns an object with the
|
|
83
|
+
following fields:
|
|
84
|
+
|
|
85
|
+
* ``current_buffer_used``: The number of items currently stored in the
|
|
86
|
+
channel buffer.
|
|
87
|
+
* ``max_buffer_size``: The maximum number of items allowed in the buffer,
|
|
88
|
+
as passed to :func:`open_memory_channel`.
|
|
89
|
+
* ``open_send_channels``: The number of open
|
|
90
|
+
:class:`MemorySendChannel` endpoints pointing to this channel.
|
|
91
|
+
Initially 1, but can be increased by
|
|
92
|
+
:meth:`MemorySendChannel.clone`.
|
|
93
|
+
* ``open_receive_channels``: Likewise, but for open
|
|
94
|
+
:class:`MemoryReceiveChannel` endpoints.
|
|
95
|
+
* ``tasks_waiting_send``: The number of tasks blocked in ``send`` on this
|
|
96
|
+
channel (summing over all clones).
|
|
97
|
+
* ``tasks_waiting_receive``: The number of tasks blocked in ``receive`` on
|
|
98
|
+
this channel (summing over all clones).
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __new__( # type: ignore[misc] # "must return a subtype"
|
|
102
|
+
cls,
|
|
103
|
+
max_buffer_size: int | float, # noqa: PYI041
|
|
104
|
+
) -> tuple[MemorySendChannel[T], MemoryReceiveChannel[T]]:
|
|
105
|
+
if max_buffer_size != inf and not isinstance(max_buffer_size, int):
|
|
106
|
+
raise TypeError("max_buffer_size must be an integer or math.inf")
|
|
107
|
+
if max_buffer_size < 0:
|
|
108
|
+
raise ValueError("max_buffer_size must be >= 0")
|
|
109
|
+
state: MemoryChannelState[T] = MemoryChannelState(max_buffer_size)
|
|
110
|
+
return (
|
|
111
|
+
MemorySendChannel[T]._create(state),
|
|
112
|
+
MemoryReceiveChannel[T]._create(state),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def __init__(self, max_buffer_size: int | float) -> None: # noqa: PYI041
|
|
116
|
+
...
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@attrs.frozen
|
|
120
|
+
class MemoryChannelStatistics:
|
|
121
|
+
current_buffer_used: int
|
|
122
|
+
max_buffer_size: int | float
|
|
123
|
+
open_send_channels: int
|
|
124
|
+
open_receive_channels: int
|
|
125
|
+
tasks_waiting_send: int
|
|
126
|
+
tasks_waiting_receive: int
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@attrs.define
|
|
130
|
+
class MemoryChannelState(Generic[T]):
|
|
131
|
+
max_buffer_size: int | float
|
|
132
|
+
data: deque[T] = attrs.Factory(deque)
|
|
133
|
+
# Counts of open endpoints using this state
|
|
134
|
+
open_send_channels: int = 0
|
|
135
|
+
open_receive_channels: int = 0
|
|
136
|
+
# {task: value}
|
|
137
|
+
send_tasks: OrderedDict[Task, T] = attrs.Factory(OrderedDict)
|
|
138
|
+
# {task: None}
|
|
139
|
+
receive_tasks: OrderedDict[Task, None] = attrs.Factory(OrderedDict)
|
|
140
|
+
|
|
141
|
+
def statistics(self) -> MemoryChannelStatistics:
|
|
142
|
+
return MemoryChannelStatistics(
|
|
143
|
+
current_buffer_used=len(self.data),
|
|
144
|
+
max_buffer_size=self.max_buffer_size,
|
|
145
|
+
open_send_channels=self.open_send_channels,
|
|
146
|
+
open_receive_channels=self.open_receive_channels,
|
|
147
|
+
tasks_waiting_send=len(self.send_tasks),
|
|
148
|
+
tasks_waiting_receive=len(self.receive_tasks),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@final
|
|
153
|
+
@attrs.define(eq=False, repr=False, slots=False)
|
|
154
|
+
class MemorySendChannel(SendChannel[SendType], metaclass=NoPublicConstructor):
|
|
155
|
+
_state: MemoryChannelState[SendType]
|
|
156
|
+
_closed: bool = False
|
|
157
|
+
# This is just the tasks waiting on *this* object. As compared to
|
|
158
|
+
# self._state.send_tasks, which includes tasks from this object and
|
|
159
|
+
# all clones.
|
|
160
|
+
_tasks: set[Task] = attrs.Factory(set)
|
|
161
|
+
|
|
162
|
+
def __attrs_post_init__(self) -> None:
|
|
163
|
+
self._state.open_send_channels += 1
|
|
164
|
+
|
|
165
|
+
def __repr__(self) -> str:
|
|
166
|
+
return f"<send channel at {id(self):#x}, using buffer at {id(self._state):#x}>"
|
|
167
|
+
|
|
168
|
+
def statistics(self) -> MemoryChannelStatistics:
|
|
169
|
+
"""Returns a `MemoryChannelStatistics` for the memory channel this is
|
|
170
|
+
associated with."""
|
|
171
|
+
# XX should we also report statistics specific to this object?
|
|
172
|
+
return self._state.statistics()
|
|
173
|
+
|
|
174
|
+
@enable_ki_protection
|
|
175
|
+
def send_nowait(self, value: SendType) -> None:
|
|
176
|
+
"""Like `~trio.abc.SendChannel.send`, but if the channel's buffer is
|
|
177
|
+
full, raises `WouldBlock` instead of blocking.
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
if self._closed:
|
|
181
|
+
raise trio.ClosedResourceError
|
|
182
|
+
if self._state.open_receive_channels == 0:
|
|
183
|
+
raise trio.BrokenResourceError
|
|
184
|
+
if self._state.receive_tasks:
|
|
185
|
+
assert not self._state.data
|
|
186
|
+
task, _ = self._state.receive_tasks.popitem(last=False)
|
|
187
|
+
task.custom_sleep_data._tasks.remove(task)
|
|
188
|
+
trio.lowlevel.reschedule(task, Value(value))
|
|
189
|
+
elif len(self._state.data) < self._state.max_buffer_size:
|
|
190
|
+
self._state.data.append(value)
|
|
191
|
+
else:
|
|
192
|
+
raise trio.WouldBlock
|
|
193
|
+
|
|
194
|
+
@enable_ki_protection
|
|
195
|
+
async def send(self, value: SendType) -> None:
|
|
196
|
+
"""See `SendChannel.send <trio.abc.SendChannel.send>`.
|
|
197
|
+
|
|
198
|
+
Memory channels allow multiple tasks to call `send` at the same time.
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
await trio.lowlevel.checkpoint_if_cancelled()
|
|
202
|
+
try:
|
|
203
|
+
self.send_nowait(value)
|
|
204
|
+
except trio.WouldBlock:
|
|
205
|
+
pass
|
|
206
|
+
else:
|
|
207
|
+
await trio.lowlevel.cancel_shielded_checkpoint()
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
task = trio.lowlevel.current_task()
|
|
211
|
+
self._tasks.add(task)
|
|
212
|
+
self._state.send_tasks[task] = value
|
|
213
|
+
task.custom_sleep_data = self
|
|
214
|
+
|
|
215
|
+
def abort_fn(_: RaiseCancelT) -> Abort:
|
|
216
|
+
self._tasks.remove(task)
|
|
217
|
+
del self._state.send_tasks[task]
|
|
218
|
+
return trio.lowlevel.Abort.SUCCEEDED
|
|
219
|
+
|
|
220
|
+
await trio.lowlevel.wait_task_rescheduled(abort_fn)
|
|
221
|
+
|
|
222
|
+
# Return type must be stringified or use a TypeVar
|
|
223
|
+
@enable_ki_protection
|
|
224
|
+
def clone(self) -> MemorySendChannel[SendType]:
|
|
225
|
+
"""Clone this send channel object.
|
|
226
|
+
|
|
227
|
+
This returns a new `MemorySendChannel` object, which acts as a
|
|
228
|
+
duplicate of the original: sending on the new object does exactly the
|
|
229
|
+
same thing as sending on the old object. (If you're familiar with
|
|
230
|
+
`os.dup`, then this is a similar idea.)
|
|
231
|
+
|
|
232
|
+
However, closing one of the objects does not close the other, and
|
|
233
|
+
receivers don't get `EndOfChannel` until *all* clones have been
|
|
234
|
+
closed.
|
|
235
|
+
|
|
236
|
+
This is useful for communication patterns that involve multiple
|
|
237
|
+
producers all sending objects to the same destination. If you give
|
|
238
|
+
each producer its own clone of the `MemorySendChannel`, and then make
|
|
239
|
+
sure to close each `MemorySendChannel` when it's finished, receivers
|
|
240
|
+
will automatically get notified when all producers are finished. See
|
|
241
|
+
:ref:`channel-mpmc` for examples.
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
trio.ClosedResourceError: if you already closed this
|
|
245
|
+
`MemorySendChannel` object.
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
if self._closed:
|
|
249
|
+
raise trio.ClosedResourceError
|
|
250
|
+
return MemorySendChannel._create(self._state)
|
|
251
|
+
|
|
252
|
+
def __enter__(self) -> Self:
|
|
253
|
+
return self
|
|
254
|
+
|
|
255
|
+
def __exit__(
|
|
256
|
+
self,
|
|
257
|
+
exc_type: type[BaseException] | None,
|
|
258
|
+
exc_value: BaseException | None,
|
|
259
|
+
traceback: TracebackType | None,
|
|
260
|
+
) -> None:
|
|
261
|
+
self.close()
|
|
262
|
+
|
|
263
|
+
@enable_ki_protection
|
|
264
|
+
def close(self) -> None:
|
|
265
|
+
"""Close this send channel object synchronously.
|
|
266
|
+
|
|
267
|
+
All channel objects have an asynchronous `~.AsyncResource.aclose` method.
|
|
268
|
+
Memory channels can also be closed synchronously. This has the same
|
|
269
|
+
effect on the channel and other tasks using it, but `close` is not a
|
|
270
|
+
trio checkpoint. This simplifies cleaning up in cancelled tasks.
|
|
271
|
+
|
|
272
|
+
Using ``with send_channel:`` will close the channel object on leaving
|
|
273
|
+
the with block.
|
|
274
|
+
|
|
275
|
+
"""
|
|
276
|
+
if self._closed:
|
|
277
|
+
return
|
|
278
|
+
self._closed = True
|
|
279
|
+
for task in self._tasks:
|
|
280
|
+
trio.lowlevel.reschedule(task, Error(trio.ClosedResourceError()))
|
|
281
|
+
del self._state.send_tasks[task]
|
|
282
|
+
self._tasks.clear()
|
|
283
|
+
self._state.open_send_channels -= 1
|
|
284
|
+
if self._state.open_send_channels == 0:
|
|
285
|
+
assert not self._state.send_tasks
|
|
286
|
+
for task in self._state.receive_tasks:
|
|
287
|
+
task.custom_sleep_data._tasks.remove(task)
|
|
288
|
+
trio.lowlevel.reschedule(task, Error(trio.EndOfChannel()))
|
|
289
|
+
self._state.receive_tasks.clear()
|
|
290
|
+
|
|
291
|
+
@enable_ki_protection
|
|
292
|
+
async def aclose(self) -> None:
|
|
293
|
+
"""Close this send channel object asynchronously.
|
|
294
|
+
|
|
295
|
+
See `MemorySendChannel.close`."""
|
|
296
|
+
self.close()
|
|
297
|
+
await trio.lowlevel.checkpoint()
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@final
|
|
301
|
+
@attrs.define(eq=False, repr=False, slots=False)
|
|
302
|
+
class MemoryReceiveChannel(ReceiveChannel[ReceiveType], metaclass=NoPublicConstructor):
|
|
303
|
+
_state: MemoryChannelState[ReceiveType]
|
|
304
|
+
_closed: bool = False
|
|
305
|
+
_tasks: set[trio._core._run.Task] = attrs.Factory(set)
|
|
306
|
+
|
|
307
|
+
def __attrs_post_init__(self) -> None:
|
|
308
|
+
self._state.open_receive_channels += 1
|
|
309
|
+
|
|
310
|
+
def statistics(self) -> MemoryChannelStatistics:
|
|
311
|
+
"""Returns a `MemoryChannelStatistics` for the memory channel this is
|
|
312
|
+
associated with."""
|
|
313
|
+
return self._state.statistics()
|
|
314
|
+
|
|
315
|
+
def __repr__(self) -> str:
|
|
316
|
+
return (
|
|
317
|
+
f"<receive channel at {id(self):#x}, using buffer at {id(self._state):#x}>"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
@enable_ki_protection
|
|
321
|
+
def receive_nowait(self) -> ReceiveType:
|
|
322
|
+
"""Like `~trio.abc.ReceiveChannel.receive`, but if there's nothing
|
|
323
|
+
ready to receive, raises `WouldBlock` instead of blocking.
|
|
324
|
+
|
|
325
|
+
"""
|
|
326
|
+
if self._closed:
|
|
327
|
+
raise trio.ClosedResourceError
|
|
328
|
+
if self._state.send_tasks:
|
|
329
|
+
task, value = self._state.send_tasks.popitem(last=False)
|
|
330
|
+
task.custom_sleep_data._tasks.remove(task)
|
|
331
|
+
trio.lowlevel.reschedule(task)
|
|
332
|
+
self._state.data.append(value)
|
|
333
|
+
# Fall through
|
|
334
|
+
if self._state.data:
|
|
335
|
+
return self._state.data.popleft()
|
|
336
|
+
if not self._state.open_send_channels:
|
|
337
|
+
raise trio.EndOfChannel
|
|
338
|
+
raise trio.WouldBlock
|
|
339
|
+
|
|
340
|
+
@enable_ki_protection
|
|
341
|
+
async def receive(self) -> ReceiveType:
|
|
342
|
+
"""See `ReceiveChannel.receive <trio.abc.ReceiveChannel.receive>`.
|
|
343
|
+
|
|
344
|
+
Memory channels allow multiple tasks to call `receive` at the same
|
|
345
|
+
time. The first task will get the first item sent, the second task
|
|
346
|
+
will get the second item sent, and so on.
|
|
347
|
+
|
|
348
|
+
"""
|
|
349
|
+
await trio.lowlevel.checkpoint_if_cancelled()
|
|
350
|
+
try:
|
|
351
|
+
value = self.receive_nowait()
|
|
352
|
+
except trio.WouldBlock:
|
|
353
|
+
pass
|
|
354
|
+
else:
|
|
355
|
+
await trio.lowlevel.cancel_shielded_checkpoint()
|
|
356
|
+
return value
|
|
357
|
+
|
|
358
|
+
task = trio.lowlevel.current_task()
|
|
359
|
+
self._tasks.add(task)
|
|
360
|
+
self._state.receive_tasks[task] = None
|
|
361
|
+
task.custom_sleep_data = self
|
|
362
|
+
|
|
363
|
+
def abort_fn(_: RaiseCancelT) -> Abort:
|
|
364
|
+
self._tasks.remove(task)
|
|
365
|
+
del self._state.receive_tasks[task]
|
|
366
|
+
return trio.lowlevel.Abort.SUCCEEDED
|
|
367
|
+
|
|
368
|
+
# Not strictly guaranteed to return ReceiveType, but will do so unless
|
|
369
|
+
# you intentionally reschedule with a bad value.
|
|
370
|
+
return await trio.lowlevel.wait_task_rescheduled(abort_fn) # type: ignore[no-any-return]
|
|
371
|
+
|
|
372
|
+
@enable_ki_protection
|
|
373
|
+
def clone(self) -> MemoryReceiveChannel[ReceiveType]:
|
|
374
|
+
"""Clone this receive channel object.
|
|
375
|
+
|
|
376
|
+
This returns a new `MemoryReceiveChannel` object, which acts as a
|
|
377
|
+
duplicate of the original: receiving on the new object does exactly
|
|
378
|
+
the same thing as receiving on the old object.
|
|
379
|
+
|
|
380
|
+
However, closing one of the objects does not close the other, and the
|
|
381
|
+
underlying channel is not closed until all clones are closed. (If
|
|
382
|
+
you're familiar with `os.dup`, then this is a similar idea.)
|
|
383
|
+
|
|
384
|
+
This is useful for communication patterns that involve multiple
|
|
385
|
+
consumers all receiving objects from the same underlying channel. See
|
|
386
|
+
:ref:`channel-mpmc` for examples.
|
|
387
|
+
|
|
388
|
+
.. warning:: The clones all share the same underlying channel.
|
|
389
|
+
Whenever a clone :meth:`receive`\\s a value, it is removed from the
|
|
390
|
+
channel and the other clones do *not* receive that value. If you
|
|
391
|
+
want to send multiple copies of the same stream of values to
|
|
392
|
+
multiple destinations, like :func:`itertools.tee`, then you need to
|
|
393
|
+
find some other solution; this method does *not* do that.
|
|
394
|
+
|
|
395
|
+
Raises:
|
|
396
|
+
trio.ClosedResourceError: if you already closed this
|
|
397
|
+
`MemoryReceiveChannel` object.
|
|
398
|
+
|
|
399
|
+
"""
|
|
400
|
+
if self._closed:
|
|
401
|
+
raise trio.ClosedResourceError
|
|
402
|
+
return MemoryReceiveChannel._create(self._state)
|
|
403
|
+
|
|
404
|
+
def __enter__(self) -> Self:
|
|
405
|
+
return self
|
|
406
|
+
|
|
407
|
+
def __exit__(
|
|
408
|
+
self,
|
|
409
|
+
exc_type: type[BaseException] | None,
|
|
410
|
+
exc_value: BaseException | None,
|
|
411
|
+
traceback: TracebackType | None,
|
|
412
|
+
) -> None:
|
|
413
|
+
self.close()
|
|
414
|
+
|
|
415
|
+
@enable_ki_protection
|
|
416
|
+
def close(self) -> None:
|
|
417
|
+
"""Close this receive channel object synchronously.
|
|
418
|
+
|
|
419
|
+
All channel objects have an asynchronous `~.AsyncResource.aclose` method.
|
|
420
|
+
Memory channels can also be closed synchronously. This has the same
|
|
421
|
+
effect on the channel and other tasks using it, but `close` is not a
|
|
422
|
+
trio checkpoint. This simplifies cleaning up in cancelled tasks.
|
|
423
|
+
|
|
424
|
+
Using ``with receive_channel:`` will close the channel object on
|
|
425
|
+
leaving the with block.
|
|
426
|
+
|
|
427
|
+
"""
|
|
428
|
+
if self._closed:
|
|
429
|
+
return
|
|
430
|
+
self._closed = True
|
|
431
|
+
for task in self._tasks:
|
|
432
|
+
trio.lowlevel.reschedule(task, Error(trio.ClosedResourceError()))
|
|
433
|
+
del self._state.receive_tasks[task]
|
|
434
|
+
self._tasks.clear()
|
|
435
|
+
self._state.open_receive_channels -= 1
|
|
436
|
+
if self._state.open_receive_channels == 0:
|
|
437
|
+
assert not self._state.receive_tasks
|
|
438
|
+
for task in self._state.send_tasks:
|
|
439
|
+
task.custom_sleep_data._tasks.remove(task)
|
|
440
|
+
trio.lowlevel.reschedule(task, Error(trio.BrokenResourceError()))
|
|
441
|
+
self._state.send_tasks.clear()
|
|
442
|
+
self._state.data.clear()
|
|
443
|
+
|
|
444
|
+
@enable_ki_protection
|
|
445
|
+
async def aclose(self) -> None:
|
|
446
|
+
"""Close this receive channel object asynchronously.
|
|
447
|
+
|
|
448
|
+
See `MemoryReceiveChannel.close`."""
|
|
449
|
+
self.close()
|
|
450
|
+
await trio.lowlevel.checkpoint()
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class RecvChanWrapper(ReceiveChannel[T]):
|
|
454
|
+
def __init__(
|
|
455
|
+
self, recv_chan: MemoryReceiveChannel[T], send_semaphore: trio.Semaphore
|
|
456
|
+
) -> None:
|
|
457
|
+
self._recv_chan = recv_chan
|
|
458
|
+
self._send_semaphore = send_semaphore
|
|
459
|
+
|
|
460
|
+
async def receive(self) -> T:
|
|
461
|
+
self._send_semaphore.release()
|
|
462
|
+
return await self._recv_chan.receive()
|
|
463
|
+
|
|
464
|
+
async def aclose(self) -> None:
|
|
465
|
+
await self._recv_chan.aclose()
|
|
466
|
+
|
|
467
|
+
def __enter__(self) -> Self:
|
|
468
|
+
return self
|
|
469
|
+
|
|
470
|
+
def __exit__(
|
|
471
|
+
self,
|
|
472
|
+
exc_type: type[BaseException] | None,
|
|
473
|
+
exc_value: BaseException | None,
|
|
474
|
+
traceback: TracebackType | None,
|
|
475
|
+
) -> None:
|
|
476
|
+
self._recv_chan.close()
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def as_safe_channel(
|
|
480
|
+
fn: Callable[P, AsyncGenerator[T, None]],
|
|
481
|
+
) -> Callable[P, AbstractAsyncContextManager[ReceiveChannel[T]]]:
|
|
482
|
+
"""Decorate an async generator function to make it cancellation-safe.
|
|
483
|
+
|
|
484
|
+
The ``yield`` keyword offers a very convenient way to write iterators...
|
|
485
|
+
which makes it really unfortunate that async generators are so difficult
|
|
486
|
+
to call correctly. Yielding from the inside of a cancel scope or a nursery
|
|
487
|
+
to the outside `violates structured concurrency <https://xkcd.com/292/>`_
|
|
488
|
+
with consequences explained in :pep:`789`. Even then, resource cleanup
|
|
489
|
+
errors remain common (:pep:`533`) unless you wrap every call in
|
|
490
|
+
:func:`~contextlib.aclosing`.
|
|
491
|
+
|
|
492
|
+
This decorator gives you the best of both worlds: with careful exception
|
|
493
|
+
handling and a background task we preserve structured concurrency by
|
|
494
|
+
offering only the safe interface, and you can still write your iterables
|
|
495
|
+
with the convenience of ``yield``. For example::
|
|
496
|
+
|
|
497
|
+
@as_safe_channel
|
|
498
|
+
async def my_async_iterable(arg, *, kwarg=True):
|
|
499
|
+
while ...:
|
|
500
|
+
item = await ...
|
|
501
|
+
yield item
|
|
502
|
+
|
|
503
|
+
async with my_async_iterable(...) as recv_chan:
|
|
504
|
+
async for item in recv_chan:
|
|
505
|
+
...
|
|
506
|
+
|
|
507
|
+
While the combined async-with-async-for can be inconvenient at first,
|
|
508
|
+
the context manager is indispensable for both correctness and for prompt
|
|
509
|
+
cleanup of resources.
|
|
510
|
+
"""
|
|
511
|
+
# Perhaps a future PEP will adopt `async with for` syntax, like
|
|
512
|
+
# https://coconut.readthedocs.io/en/master/DOCS.html#async-with-for
|
|
513
|
+
|
|
514
|
+
@asynccontextmanager
|
|
515
|
+
@wraps(fn)
|
|
516
|
+
async def context_manager(
|
|
517
|
+
*args: P.args, **kwargs: P.kwargs
|
|
518
|
+
) -> AsyncGenerator[trio._channel.RecvChanWrapper[T], None]:
|
|
519
|
+
send_chan, recv_chan = trio.open_memory_channel[T](0)
|
|
520
|
+
try:
|
|
521
|
+
async with trio.open_nursery(strict_exception_groups=True) as nursery:
|
|
522
|
+
agen = fn(*args, **kwargs)
|
|
523
|
+
send_semaphore = trio.Semaphore(0)
|
|
524
|
+
# `nursery.start` to make sure that we will clean up send_chan & agen
|
|
525
|
+
# If this errors we don't close `recv_chan`, but the caller
|
|
526
|
+
# never gets access to it, so that's not a problem.
|
|
527
|
+
await nursery.start(
|
|
528
|
+
_move_elems_to_channel, agen, send_chan, send_semaphore
|
|
529
|
+
)
|
|
530
|
+
# `async with recv_chan` could eat exceptions, so use sync cm
|
|
531
|
+
with RecvChanWrapper(recv_chan, send_semaphore) as wrapped_recv_chan:
|
|
532
|
+
yield wrapped_recv_chan
|
|
533
|
+
# User has exited context manager, cancel to immediately close the
|
|
534
|
+
# abandoned generator if it's still alive.
|
|
535
|
+
nursery.cancel_scope.cancel(
|
|
536
|
+
"exited trio.as_safe_channel context manager"
|
|
537
|
+
)
|
|
538
|
+
except BaseExceptionGroup as eg:
|
|
539
|
+
try:
|
|
540
|
+
raise_single_exception_from_group(eg)
|
|
541
|
+
except MultipleExceptionError:
|
|
542
|
+
# In case user has except* we make it possible for them to handle the
|
|
543
|
+
# exceptions.
|
|
544
|
+
if sys.version_info >= (3, 11):
|
|
545
|
+
eg.add_note(
|
|
546
|
+
"Encountered exception during cleanup of generator object, as "
|
|
547
|
+
"well as exception in the contextmanager body - unable to unwrap."
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
raise eg from None
|
|
551
|
+
|
|
552
|
+
async def _move_elems_to_channel(
|
|
553
|
+
agen: AsyncGenerator[T, None],
|
|
554
|
+
send_chan: trio.MemorySendChannel[T],
|
|
555
|
+
send_semaphore: trio.Semaphore,
|
|
556
|
+
task_status: trio.TaskStatus,
|
|
557
|
+
) -> None:
|
|
558
|
+
# `async with send_chan` will eat exceptions,
|
|
559
|
+
# see https://github.com/python-trio/trio/issues/1559
|
|
560
|
+
with send_chan:
|
|
561
|
+
# replace try-finally with contextlib.aclosing once python39 is
|
|
562
|
+
# dropped:
|
|
563
|
+
try:
|
|
564
|
+
task_status.started()
|
|
565
|
+
while True:
|
|
566
|
+
# wait for receiver to call next on the aiter
|
|
567
|
+
await send_semaphore.acquire()
|
|
568
|
+
if not send_chan._state.open_receive_channels:
|
|
569
|
+
# skip the possibly-expensive computation in the generator,
|
|
570
|
+
# if we know it will be impossible to send the result.
|
|
571
|
+
break
|
|
572
|
+
try:
|
|
573
|
+
value = await agen.__anext__()
|
|
574
|
+
except StopAsyncIteration:
|
|
575
|
+
return
|
|
576
|
+
# Send the value to the channel
|
|
577
|
+
try:
|
|
578
|
+
await send_chan.send(value)
|
|
579
|
+
except BrokenResourceError:
|
|
580
|
+
break # closed since we checked above
|
|
581
|
+
finally:
|
|
582
|
+
# work around `.aclose()` not suppressing GeneratorExit in an
|
|
583
|
+
# ExceptionGroup:
|
|
584
|
+
# TODO: make an issue on CPython about this
|
|
585
|
+
try:
|
|
586
|
+
await agen.aclose()
|
|
587
|
+
except BaseExceptionGroup as exceptions:
|
|
588
|
+
removed, narrowed_exceptions = exceptions.split(GeneratorExit)
|
|
589
|
+
|
|
590
|
+
# TODO: extract a helper to flatten exception groups
|
|
591
|
+
removed_exceptions: list[BaseException | None] = [removed]
|
|
592
|
+
genexits_seen = 0
|
|
593
|
+
for e in removed_exceptions:
|
|
594
|
+
if isinstance(e, BaseExceptionGroup):
|
|
595
|
+
removed_exceptions.extend(e.exceptions) # noqa: B909
|
|
596
|
+
else:
|
|
597
|
+
genexits_seen += 1
|
|
598
|
+
|
|
599
|
+
if genexits_seen > 1:
|
|
600
|
+
exc = AssertionError("More than one GeneratorExit found.")
|
|
601
|
+
if narrowed_exceptions is None:
|
|
602
|
+
narrowed_exceptions = exceptions.derive([exc])
|
|
603
|
+
else:
|
|
604
|
+
narrowed_exceptions = narrowed_exceptions.derive(
|
|
605
|
+
[*narrowed_exceptions.exceptions, exc]
|
|
606
|
+
)
|
|
607
|
+
if narrowed_exceptions is not None:
|
|
608
|
+
raise narrowed_exceptions from None
|
|
609
|
+
|
|
610
|
+
return context_manager
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This namespace represents the core functionality that has to be built-in
|
|
3
|
+
and deal with private internal data structures. Things in this namespace
|
|
4
|
+
are publicly available in either trio, trio.lowlevel, or trio.testing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import typing as _t
|
|
9
|
+
|
|
10
|
+
from ._entry_queue import TrioToken
|
|
11
|
+
from ._exceptions import (
|
|
12
|
+
BrokenResourceError,
|
|
13
|
+
BusyResourceError,
|
|
14
|
+
Cancelled,
|
|
15
|
+
ClosedResourceError,
|
|
16
|
+
EndOfChannel,
|
|
17
|
+
RunFinishedError,
|
|
18
|
+
TrioInternalError,
|
|
19
|
+
WouldBlock,
|
|
20
|
+
)
|
|
21
|
+
from ._ki import currently_ki_protected, disable_ki_protection, enable_ki_protection
|
|
22
|
+
from ._local import RunVar, RunVarToken
|
|
23
|
+
from ._mock_clock import MockClock
|
|
24
|
+
from ._parking_lot import (
|
|
25
|
+
ParkingLot,
|
|
26
|
+
ParkingLotStatistics,
|
|
27
|
+
add_parking_lot_breaker,
|
|
28
|
+
remove_parking_lot_breaker,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Imports that always exist
|
|
32
|
+
from ._run import (
|
|
33
|
+
TASK_STATUS_IGNORED,
|
|
34
|
+
CancelScope,
|
|
35
|
+
Nursery,
|
|
36
|
+
RunStatistics,
|
|
37
|
+
Task,
|
|
38
|
+
TaskStatus,
|
|
39
|
+
add_instrument,
|
|
40
|
+
checkpoint,
|
|
41
|
+
checkpoint_if_cancelled,
|
|
42
|
+
current_clock,
|
|
43
|
+
current_effective_deadline,
|
|
44
|
+
current_root_task,
|
|
45
|
+
current_statistics,
|
|
46
|
+
current_task,
|
|
47
|
+
current_time,
|
|
48
|
+
current_trio_token,
|
|
49
|
+
in_trio_run,
|
|
50
|
+
in_trio_task,
|
|
51
|
+
notify_closing,
|
|
52
|
+
open_nursery,
|
|
53
|
+
remove_instrument,
|
|
54
|
+
reschedule,
|
|
55
|
+
run,
|
|
56
|
+
spawn_system_task,
|
|
57
|
+
start_guest_run,
|
|
58
|
+
wait_all_tasks_blocked,
|
|
59
|
+
wait_readable,
|
|
60
|
+
wait_writable,
|
|
61
|
+
)
|
|
62
|
+
from ._thread_cache import start_thread_soon
|
|
63
|
+
|
|
64
|
+
# Has to come after _run to resolve a circular import
|
|
65
|
+
from ._traps import (
|
|
66
|
+
Abort,
|
|
67
|
+
RaiseCancelT,
|
|
68
|
+
cancel_shielded_checkpoint,
|
|
69
|
+
permanently_detach_coroutine_object,
|
|
70
|
+
reattach_detached_coroutine_object,
|
|
71
|
+
temporarily_detach_coroutine_object,
|
|
72
|
+
wait_task_rescheduled,
|
|
73
|
+
)
|
|
74
|
+
from ._unbounded_queue import UnboundedQueue, UnboundedQueueStatistics
|
|
75
|
+
|
|
76
|
+
# Windows imports
|
|
77
|
+
if sys.platform == "win32" or (
|
|
78
|
+
not _t.TYPE_CHECKING and "sphinx.ext.autodoc" in sys.modules
|
|
79
|
+
):
|
|
80
|
+
from ._run import (
|
|
81
|
+
current_iocp,
|
|
82
|
+
monitor_completion_key,
|
|
83
|
+
readinto_overlapped,
|
|
84
|
+
register_with_iocp,
|
|
85
|
+
wait_overlapped,
|
|
86
|
+
write_overlapped,
|
|
87
|
+
)
|
|
88
|
+
# Kqueue imports
|
|
89
|
+
if (
|
|
90
|
+
sys.platform != "linux" and sys.platform != "win32" and sys.platform != "android"
|
|
91
|
+
) or (not _t.TYPE_CHECKING and "sphinx.ext.autodoc" in sys.modules):
|
|
92
|
+
from ._run import current_kqueue, monitor_kevent, wait_kevent
|
|
93
|
+
|
|
94
|
+
del sys # It would be better to import sys as _sys, but mypy does not understand it
|