@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.
Files changed (196) hide show
  1. package/bin/kgraph-launcher +15 -3
  2. package/lib/kgraph/scripts/build-bundle.sh +17 -4
  3. package/lib/site-packages/outcome/__init__.py +20 -0
  4. package/lib/site-packages/outcome/_impl.py +239 -0
  5. package/lib/site-packages/outcome/_util.py +33 -0
  6. package/lib/site-packages/outcome/_version.py +7 -0
  7. package/lib/site-packages/outcome/py.typed +0 -0
  8. package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
  9. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
  10. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
  11. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
  12. package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
  13. package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
  14. package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
  15. package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
  16. package/lib/site-packages/sniffio/__init__.py +17 -0
  17. package/lib/site-packages/sniffio/_impl.py +95 -0
  18. package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
  19. package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
  20. package/lib/site-packages/sniffio/_version.py +3 -0
  21. package/lib/site-packages/sniffio/py.typed +0 -0
  22. package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
  23. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
  24. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
  25. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
  26. package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
  27. package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
  28. package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
  29. package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
  30. package/lib/site-packages/sortedcontainers/__init__.py +74 -0
  31. package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
  32. package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
  33. package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
  34. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
  35. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
  36. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
  37. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
  38. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
  39. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
  40. package/lib/site-packages/trio/__init__.py +133 -0
  41. package/lib/site-packages/trio/__main__.py +3 -0
  42. package/lib/site-packages/trio/_abc.py +714 -0
  43. package/lib/site-packages/trio/_channel.py +610 -0
  44. package/lib/site-packages/trio/_core/__init__.py +94 -0
  45. package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
  46. package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
  47. package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
  48. package/lib/site-packages/trio/_core/_exceptions.py +169 -0
  49. package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
  50. package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
  51. package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
  52. package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
  53. package/lib/site-packages/trio/_core/_generated_run.py +269 -0
  54. package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
  55. package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
  56. package/lib/site-packages/trio/_core/_io_common.py +31 -0
  57. package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
  58. package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
  59. package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
  60. package/lib/site-packages/trio/_core/_ki.py +271 -0
  61. package/lib/site-packages/trio/_core/_local.py +104 -0
  62. package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
  63. package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
  64. package/lib/site-packages/trio/_core/_run.py +3148 -0
  65. package/lib/site-packages/trio/_core/_run_context.py +15 -0
  66. package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
  67. package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
  68. package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
  69. package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
  70. package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
  71. package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
  72. package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
  73. package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
  74. package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
  75. package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
  76. package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
  77. package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
  78. package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
  79. package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
  80. package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
  81. package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
  82. package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
  83. package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
  84. package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
  85. package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
  86. package/lib/site-packages/trio/_core/_traps.py +318 -0
  87. package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
  88. package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
  89. package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
  90. package/lib/site-packages/trio/_deprecate.py +171 -0
  91. package/lib/site-packages/trio/_dtls.py +1380 -0
  92. package/lib/site-packages/trio/_file_io.py +513 -0
  93. package/lib/site-packages/trio/_highlevel_generic.py +125 -0
  94. package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
  95. package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
  96. package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
  97. package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
  98. package/lib/site-packages/trio/_highlevel_socket.py +423 -0
  99. package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
  100. package/lib/site-packages/trio/_path.py +289 -0
  101. package/lib/site-packages/trio/_repl.py +159 -0
  102. package/lib/site-packages/trio/_signals.py +185 -0
  103. package/lib/site-packages/trio/_socket.py +1326 -0
  104. package/lib/site-packages/trio/_ssl.py +964 -0
  105. package/lib/site-packages/trio/_subprocess.py +1178 -0
  106. package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
  107. package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
  108. package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
  109. package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
  110. package/lib/site-packages/trio/_sync.py +908 -0
  111. package/lib/site-packages/trio/_tests/__init__.py +0 -0
  112. package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
  113. package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
  114. package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
  115. package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
  116. package/lib/site-packages/trio/_tests/test_abc.py +72 -0
  117. package/lib/site-packages/trio/_tests/test_channel.py +750 -0
  118. package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
  119. package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
  120. package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
  121. package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
  122. package/lib/site-packages/trio/_tests/test_exports.py +626 -0
  123. package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
  124. package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
  125. package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
  126. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
  127. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
  128. package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
  129. package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
  130. package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
  131. package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
  132. package/lib/site-packages/trio/_tests/test_path.py +279 -0
  133. package/lib/site-packages/trio/_tests/test_repl.py +428 -0
  134. package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
  135. package/lib/site-packages/trio/_tests/test_signals.py +186 -0
  136. package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
  137. package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
  138. package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
  139. package/lib/site-packages/trio/_tests/test_sync.py +735 -0
  140. package/lib/site-packages/trio/_tests/test_testing.py +682 -0
  141. package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
  142. package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
  143. package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
  144. package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
  145. package/lib/site-packages/trio/_tests/test_trio.py +8 -0
  146. package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
  147. package/lib/site-packages/trio/_tests/test_util.py +349 -0
  148. package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
  149. package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
  150. package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
  151. package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
  152. package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
  153. package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
  154. package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
  155. package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
  156. package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
  157. package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
  158. package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
  159. package/lib/site-packages/trio/_threads.py +610 -0
  160. package/lib/site-packages/trio/_timeouts.py +197 -0
  161. package/lib/site-packages/trio/_tools/__init__.py +0 -0
  162. package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
  163. package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
  164. package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
  165. package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
  166. package/lib/site-packages/trio/_unix_pipes.py +197 -0
  167. package/lib/site-packages/trio/_util.py +385 -0
  168. package/lib/site-packages/trio/_version.py +3 -0
  169. package/lib/site-packages/trio/_wait_for_object.py +67 -0
  170. package/lib/site-packages/trio/_windows_pipes.py +144 -0
  171. package/lib/site-packages/trio/abc.py +23 -0
  172. package/lib/site-packages/trio/from_thread.py +13 -0
  173. package/lib/site-packages/trio/lowlevel.py +95 -0
  174. package/lib/site-packages/trio/py.typed +0 -0
  175. package/lib/site-packages/trio/socket.py +602 -0
  176. package/lib/site-packages/trio/testing/__init__.py +58 -0
  177. package/lib/site-packages/trio/testing/_check_streams.py +570 -0
  178. package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
  179. package/lib/site-packages/trio/testing/_fake_net.py +584 -0
  180. package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
  181. package/lib/site-packages/trio/testing/_network.py +36 -0
  182. package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
  183. package/lib/site-packages/trio/testing/_sequencer.py +87 -0
  184. package/lib/site-packages/trio/testing/_trio_test.py +50 -0
  185. package/lib/site-packages/trio/to_thread.py +4 -0
  186. package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
  187. package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
  188. package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
  189. package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
  190. package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
  191. package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
  192. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
  193. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
  194. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
  195. package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
  196. 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