@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,633 @@
1
+ from __future__ import annotations
2
+
3
+ import operator
4
+ from collections.abc import Awaitable, Callable
5
+ from typing import TypeAlias, TypeVar
6
+
7
+ from .. import _core, _util
8
+ from .._highlevel_generic import StapledStream
9
+ from ..abc import ReceiveStream, SendStream
10
+
11
+ AsyncHook: TypeAlias = Callable[[], Awaitable[object]]
12
+ # Would be nice to exclude awaitable here, but currently not possible.
13
+ SyncHook: TypeAlias = Callable[[], object]
14
+ SendStreamT = TypeVar("SendStreamT", bound=SendStream)
15
+ ReceiveStreamT = TypeVar("ReceiveStreamT", bound=ReceiveStream)
16
+
17
+
18
+ ################################################################
19
+ # In-memory streams - Unbounded buffer version
20
+ ################################################################
21
+
22
+
23
+ class _UnboundedByteQueue:
24
+ def __init__(self) -> None:
25
+ self._data = bytearray()
26
+ self._closed = False
27
+ self._lot = _core.ParkingLot()
28
+ self._fetch_lock = _util.ConflictDetector(
29
+ "another task is already fetching data",
30
+ )
31
+
32
+ # This object treats "close" as being like closing the send side of a
33
+ # channel: so after close(), calling put() raises ClosedResourceError, and
34
+ # calling the get() variants drains the buffer and then returns an empty
35
+ # bytearray.
36
+ def close(self) -> None:
37
+ self._closed = True
38
+ self._lot.unpark_all()
39
+
40
+ def close_and_wipe(self) -> None:
41
+ self._data = bytearray()
42
+ self.close()
43
+
44
+ def put(self, data: bytes | bytearray | memoryview) -> None:
45
+ if self._closed:
46
+ raise _core.ClosedResourceError("virtual connection closed")
47
+ self._data += data
48
+ self._lot.unpark_all()
49
+
50
+ def _check_max_bytes(self, max_bytes: int | None) -> None:
51
+ if max_bytes is None:
52
+ return
53
+ max_bytes = operator.index(max_bytes)
54
+ if max_bytes < 1:
55
+ raise ValueError("max_bytes must be >= 1")
56
+
57
+ def _get_impl(self, max_bytes: int | None) -> bytearray:
58
+ assert self._closed or self._data
59
+ if max_bytes is None:
60
+ max_bytes = len(self._data)
61
+ if self._data:
62
+ chunk = self._data[:max_bytes]
63
+ del self._data[:max_bytes]
64
+ assert chunk
65
+ return chunk
66
+ else:
67
+ return bytearray()
68
+
69
+ def get_nowait(self, max_bytes: int | None = None) -> bytearray:
70
+ with self._fetch_lock:
71
+ self._check_max_bytes(max_bytes)
72
+ if not self._closed and not self._data:
73
+ raise _core.WouldBlock
74
+ return self._get_impl(max_bytes)
75
+
76
+ async def get(self, max_bytes: int | None = None) -> bytearray:
77
+ with self._fetch_lock:
78
+ self._check_max_bytes(max_bytes)
79
+ if not self._closed and not self._data:
80
+ await self._lot.park()
81
+ else:
82
+ await _core.checkpoint()
83
+ return self._get_impl(max_bytes)
84
+
85
+
86
+ @_util.final
87
+ class MemorySendStream(SendStream):
88
+ """An in-memory :class:`~trio.abc.SendStream`.
89
+
90
+ Args:
91
+ send_all_hook: An async function, or None. Called from
92
+ :meth:`send_all`. Can do whatever you like.
93
+ wait_send_all_might_not_block_hook: An async function, or None. Called
94
+ from :meth:`wait_send_all_might_not_block`. Can do whatever you
95
+ like.
96
+ close_hook: A synchronous function, or None. Called from :meth:`close`
97
+ and :meth:`aclose`. Can do whatever you like.
98
+
99
+ .. attribute:: send_all_hook
100
+ wait_send_all_might_not_block_hook
101
+ close_hook
102
+
103
+ All of these hooks are also exposed as attributes on the object, and
104
+ you can change them at any time.
105
+
106
+ """
107
+
108
+ def __init__(
109
+ self,
110
+ send_all_hook: AsyncHook | None = None,
111
+ wait_send_all_might_not_block_hook: AsyncHook | None = None,
112
+ close_hook: SyncHook | None = None,
113
+ ) -> None:
114
+ self._conflict_detector = _util.ConflictDetector(
115
+ "another task is using this stream",
116
+ )
117
+ self._outgoing = _UnboundedByteQueue()
118
+ self.send_all_hook = send_all_hook
119
+ self.wait_send_all_might_not_block_hook = wait_send_all_might_not_block_hook
120
+ self.close_hook = close_hook
121
+
122
+ async def send_all(self, data: bytes | bytearray | memoryview) -> None:
123
+ """Places the given data into the object's internal buffer, and then
124
+ calls the :attr:`send_all_hook` (if any).
125
+
126
+ """
127
+ # Execute two checkpoints so we have more of a chance to detect
128
+ # buggy user code that calls this twice at the same time.
129
+ with self._conflict_detector:
130
+ await _core.checkpoint()
131
+ await _core.checkpoint()
132
+ self._outgoing.put(data)
133
+ if self.send_all_hook is not None:
134
+ await self.send_all_hook()
135
+
136
+ async def wait_send_all_might_not_block(self) -> None:
137
+ """Calls the :attr:`wait_send_all_might_not_block_hook` (if any), and
138
+ then returns immediately.
139
+
140
+ """
141
+ # Execute two checkpoints so that we have more of a chance to detect
142
+ # buggy user code that calls this twice at the same time.
143
+ with self._conflict_detector:
144
+ await _core.checkpoint()
145
+ await _core.checkpoint()
146
+ # check for being closed:
147
+ self._outgoing.put(b"")
148
+ if self.wait_send_all_might_not_block_hook is not None:
149
+ await self.wait_send_all_might_not_block_hook()
150
+
151
+ def close(self) -> None:
152
+ """Marks this stream as closed, and then calls the :attr:`close_hook`
153
+ (if any).
154
+
155
+ """
156
+ # XXX should this cancel any pending calls to the send_all_hook and
157
+ # wait_send_all_might_not_block_hook? Those are the only places where
158
+ # send_all and wait_send_all_might_not_block can be blocked.
159
+ #
160
+ # The way we set things up, send_all_hook is memory_stream_pump, and
161
+ # wait_send_all_might_not_block_hook is unset. memory_stream_pump is
162
+ # synchronous. So normally, send_all and wait_send_all_might_not_block
163
+ # cannot block at all.
164
+ self._outgoing.close()
165
+ if self.close_hook is not None:
166
+ self.close_hook()
167
+
168
+ async def aclose(self) -> None:
169
+ """Same as :meth:`close`, but async."""
170
+ self.close()
171
+ await _core.checkpoint()
172
+
173
+ async def get_data(self, max_bytes: int | None = None) -> bytearray:
174
+ """Retrieves data from the internal buffer, blocking if necessary.
175
+
176
+ Args:
177
+ max_bytes (int or None): The maximum amount of data to
178
+ retrieve. None (the default) means to retrieve all the data
179
+ that's present (but still blocks until at least one byte is
180
+ available).
181
+
182
+ Returns:
183
+ If this stream has been closed, an empty bytearray. Otherwise, the
184
+ requested data.
185
+
186
+ """
187
+ return await self._outgoing.get(max_bytes)
188
+
189
+ def get_data_nowait(self, max_bytes: int | None = None) -> bytearray:
190
+ """Retrieves data from the internal buffer, but doesn't block.
191
+
192
+ See :meth:`get_data` for details.
193
+
194
+ Raises:
195
+ trio.WouldBlock: if no data is available to retrieve.
196
+
197
+ """
198
+ return self._outgoing.get_nowait(max_bytes)
199
+
200
+
201
+ @_util.final
202
+ class MemoryReceiveStream(ReceiveStream):
203
+ """An in-memory :class:`~trio.abc.ReceiveStream`.
204
+
205
+ Args:
206
+ receive_some_hook: An async function, or None. Called from
207
+ :meth:`receive_some`. Can do whatever you like.
208
+ close_hook: A synchronous function, or None. Called from :meth:`close`
209
+ and :meth:`aclose`. Can do whatever you like.
210
+
211
+ .. attribute:: receive_some_hook
212
+ close_hook
213
+
214
+ Both hooks are also exposed as attributes on the object, and you can
215
+ change them at any time.
216
+
217
+ """
218
+
219
+ def __init__(
220
+ self,
221
+ receive_some_hook: AsyncHook | None = None,
222
+ close_hook: SyncHook | None = None,
223
+ ) -> None:
224
+ self._conflict_detector = _util.ConflictDetector(
225
+ "another task is using this stream",
226
+ )
227
+ self._incoming = _UnboundedByteQueue()
228
+ self._closed = False
229
+ self.receive_some_hook = receive_some_hook
230
+ self.close_hook = close_hook
231
+
232
+ async def receive_some(self, max_bytes: int | None = None) -> bytearray:
233
+ """Calls the :attr:`receive_some_hook` (if any), and then retrieves
234
+ data from the internal buffer, blocking if necessary.
235
+
236
+ """
237
+ # Execute two checkpoints so we have more of a chance to detect
238
+ # buggy user code that calls this twice at the same time.
239
+ with self._conflict_detector:
240
+ await _core.checkpoint()
241
+ await _core.checkpoint()
242
+ if self._closed:
243
+ raise _core.ClosedResourceError
244
+ if self.receive_some_hook is not None:
245
+ await self.receive_some_hook()
246
+ # self._incoming's closure state tracks whether we got an EOF.
247
+ # self._closed tracks whether we, ourselves, are closed.
248
+ # self.close() sends an EOF to wake us up and sets self._closed,
249
+ # so after we wake up we have to check self._closed again.
250
+ data = await self._incoming.get(max_bytes)
251
+ if self._closed:
252
+ raise _core.ClosedResourceError
253
+ return data
254
+
255
+ def close(self) -> None:
256
+ """Discards any pending data from the internal buffer, and marks this
257
+ stream as closed.
258
+
259
+ """
260
+ self._closed = True
261
+ self._incoming.close_and_wipe()
262
+ if self.close_hook is not None:
263
+ self.close_hook()
264
+
265
+ async def aclose(self) -> None:
266
+ """Same as :meth:`close`, but async."""
267
+ self.close()
268
+ await _core.checkpoint()
269
+
270
+ def put_data(self, data: bytes | bytearray | memoryview) -> None:
271
+ """Appends the given data to the internal buffer."""
272
+ self._incoming.put(data)
273
+
274
+ def put_eof(self) -> None:
275
+ """Adds an end-of-file marker to the internal buffer."""
276
+ self._incoming.close()
277
+
278
+
279
+ # TODO: investigate why this is necessary for the docs
280
+ MemorySendStream.__module__ = MemorySendStream.__module__.replace(
281
+ "._memory_streams", ""
282
+ )
283
+ MemoryReceiveStream.__module__ = MemoryReceiveStream.__module__.replace(
284
+ "._memory_streams", ""
285
+ )
286
+
287
+
288
+ def memory_stream_pump(
289
+ memory_send_stream: MemorySendStream,
290
+ memory_receive_stream: MemoryReceiveStream,
291
+ *,
292
+ max_bytes: int | None = None,
293
+ ) -> bool:
294
+ """Take data out of the given :class:`MemorySendStream`'s internal buffer,
295
+ and put it into the given :class:`MemoryReceiveStream`'s internal buffer.
296
+
297
+ Args:
298
+ memory_send_stream (MemorySendStream): The stream to get data from.
299
+ memory_receive_stream (MemoryReceiveStream): The stream to put data into.
300
+ max_bytes (int or None): The maximum amount of data to transfer in this
301
+ call, or None to transfer all available data.
302
+
303
+ Returns:
304
+ True if it successfully transferred some data, or False if there was no
305
+ data to transfer.
306
+
307
+ This is used to implement :func:`memory_stream_one_way_pair` and
308
+ :func:`memory_stream_pair`; see the latter's docstring for an example
309
+ of how you might use it yourself.
310
+
311
+ """
312
+ try:
313
+ data = memory_send_stream.get_data_nowait(max_bytes)
314
+ except _core.WouldBlock:
315
+ return False
316
+ try:
317
+ if not data:
318
+ memory_receive_stream.put_eof()
319
+ else:
320
+ memory_receive_stream.put_data(data)
321
+ except _core.ClosedResourceError:
322
+ raise _core.BrokenResourceError("MemoryReceiveStream was closed") from None
323
+ return True
324
+
325
+
326
+ def memory_stream_one_way_pair() -> tuple[MemorySendStream, MemoryReceiveStream]:
327
+ """Create a connected, pure-Python, unidirectional stream with infinite
328
+ buffering and flexible configuration options.
329
+
330
+ You can think of this as being a no-operating-system-involved
331
+ Trio-streamsified version of :func:`os.pipe` (except that :func:`os.pipe`
332
+ returns the streams in the wrong order – we follow the superior convention
333
+ that data flows from left to right).
334
+
335
+ Returns:
336
+ A tuple (:class:`MemorySendStream`, :class:`MemoryReceiveStream`), where
337
+ the :class:`MemorySendStream` has its hooks set up so that it calls
338
+ :func:`memory_stream_pump` from its
339
+ :attr:`~MemorySendStream.send_all_hook` and
340
+ :attr:`~MemorySendStream.close_hook`.
341
+
342
+ The end result is that data automatically flows from the
343
+ :class:`MemorySendStream` to the :class:`MemoryReceiveStream`. But you're
344
+ also free to rearrange things however you like. For example, you can
345
+ temporarily set the :attr:`~MemorySendStream.send_all_hook` to None if you
346
+ want to simulate a stall in data transmission. Or see
347
+ :func:`memory_stream_pair` for a more elaborate example.
348
+
349
+ """
350
+ send_stream = MemorySendStream()
351
+ recv_stream = MemoryReceiveStream()
352
+
353
+ def pump_from_send_stream_to_recv_stream() -> None:
354
+ memory_stream_pump(send_stream, recv_stream)
355
+
356
+ # await not used
357
+ async def async_pump_from_send_stream_to_recv_stream() -> None: # noqa: RUF029
358
+ pump_from_send_stream_to_recv_stream()
359
+
360
+ send_stream.send_all_hook = async_pump_from_send_stream_to_recv_stream
361
+ send_stream.close_hook = pump_from_send_stream_to_recv_stream
362
+ return send_stream, recv_stream
363
+
364
+
365
+ def _make_stapled_pair(
366
+ one_way_pair: Callable[[], tuple[SendStreamT, ReceiveStreamT]],
367
+ ) -> tuple[
368
+ StapledStream[SendStreamT, ReceiveStreamT],
369
+ StapledStream[SendStreamT, ReceiveStreamT],
370
+ ]:
371
+ pipe1_send, pipe1_recv = one_way_pair()
372
+ pipe2_send, pipe2_recv = one_way_pair()
373
+ stream1 = StapledStream(pipe1_send, pipe2_recv)
374
+ stream2 = StapledStream(pipe2_send, pipe1_recv)
375
+ return stream1, stream2
376
+
377
+
378
+ def memory_stream_pair() -> tuple[
379
+ StapledStream[MemorySendStream, MemoryReceiveStream],
380
+ StapledStream[MemorySendStream, MemoryReceiveStream],
381
+ ]:
382
+ """Create a connected, pure-Python, bidirectional stream with infinite
383
+ buffering and flexible configuration options.
384
+
385
+ This is a convenience function that creates two one-way streams using
386
+ :func:`memory_stream_one_way_pair`, and then uses
387
+ :class:`~trio.StapledStream` to combine them into a single bidirectional
388
+ stream.
389
+
390
+ This is like a no-operating-system-involved, Trio-streamsified version of
391
+ :func:`socket.socketpair`.
392
+
393
+ Returns:
394
+ A pair of :class:`~trio.StapledStream` objects that are connected so
395
+ that data automatically flows from one to the other in both directions.
396
+
397
+ After creating a stream pair, you can send data back and forth, which is
398
+ enough for simple tests::
399
+
400
+ left, right = memory_stream_pair()
401
+ await left.send_all(b"123")
402
+ assert await right.receive_some() == b"123"
403
+ await right.send_all(b"456")
404
+ assert await left.receive_some() == b"456"
405
+
406
+ But if you read the docs for :class:`~trio.StapledStream` and
407
+ :func:`memory_stream_one_way_pair`, you'll see that all the pieces
408
+ involved in wiring this up are public APIs, so you can adjust to suit the
409
+ requirements of your tests. For example, here's how to tweak a stream so
410
+ that data flowing from left to right trickles in one byte at a time (but
411
+ data flowing from right to left proceeds at full speed)::
412
+
413
+ left, right = memory_stream_pair()
414
+ async def trickle():
415
+ # left is a StapledStream, and left.send_stream is a MemorySendStream
416
+ # right is a StapledStream, and right.recv_stream is a MemoryReceiveStream
417
+ while memory_stream_pump(left.send_stream, right.recv_stream, max_bytes=1):
418
+ # Pause between each byte
419
+ await trio.sleep(1)
420
+ # Normally this send_all_hook calls memory_stream_pump directly without
421
+ # passing in a max_bytes. We replace it with our custom version:
422
+ left.send_stream.send_all_hook = trickle
423
+
424
+ And here's a simple test using our modified stream objects::
425
+
426
+ async def sender():
427
+ await left.send_all(b"12345")
428
+ await left.send_eof()
429
+
430
+ async def receiver():
431
+ async for data in right:
432
+ print(data)
433
+
434
+ async with trio.open_nursery() as nursery:
435
+ nursery.start_soon(sender)
436
+ nursery.start_soon(receiver)
437
+
438
+ By default, this will print ``b"12345"`` and then immediately exit; with
439
+ our trickle stream it instead sleeps 1 second, then prints ``b"1"``, then
440
+ sleeps 1 second, then prints ``b"2"``, etc.
441
+
442
+ Pro-tip: you can insert sleep calls (like in our example above) to
443
+ manipulate the flow of data across tasks... and then use
444
+ :class:`MockClock` and its :attr:`~MockClock.autojump_threshold`
445
+ functionality to keep your test suite running quickly.
446
+
447
+ If you want to stress test a protocol implementation, one nice trick is to
448
+ use the :mod:`random` module (preferably with a fixed seed) to move random
449
+ numbers of bytes at a time, and insert random sleeps in between them. You
450
+ can also set up a custom :attr:`~MemoryReceiveStream.receive_some_hook` if
451
+ you want to manipulate things on the receiving side, and not just the
452
+ sending side.
453
+
454
+ """
455
+ return _make_stapled_pair(memory_stream_one_way_pair)
456
+
457
+
458
+ ################################################################
459
+ # In-memory streams - Lockstep version
460
+ ################################################################
461
+
462
+
463
+ class _LockstepByteQueue:
464
+ def __init__(self) -> None:
465
+ self._data = bytearray()
466
+ self._sender_closed = False
467
+ self._receiver_closed = False
468
+ self._receiver_waiting = False
469
+ self._waiters = _core.ParkingLot()
470
+ self._send_conflict_detector = _util.ConflictDetector(
471
+ "another task is already sending",
472
+ )
473
+ self._receive_conflict_detector = _util.ConflictDetector(
474
+ "another task is already receiving",
475
+ )
476
+
477
+ def _something_happened(self) -> None:
478
+ self._waiters.unpark_all()
479
+
480
+ # Always wakes up when one side is closed, because everyone always reacts
481
+ # to that.
482
+ async def _wait_for(self, fn: Callable[[], bool]) -> None:
483
+ while True:
484
+ if fn():
485
+ break
486
+ if self._sender_closed or self._receiver_closed:
487
+ break
488
+ await self._waiters.park()
489
+ await _core.checkpoint()
490
+
491
+ def close_sender(self) -> None:
492
+ self._sender_closed = True
493
+ self._something_happened()
494
+
495
+ def close_receiver(self) -> None:
496
+ self._receiver_closed = True
497
+ self._something_happened()
498
+
499
+ async def send_all(self, data: bytes | bytearray | memoryview) -> None:
500
+ with self._send_conflict_detector:
501
+ if self._sender_closed:
502
+ raise _core.ClosedResourceError
503
+ if self._receiver_closed:
504
+ raise _core.BrokenResourceError
505
+ assert not self._data
506
+ self._data += data
507
+ self._something_happened()
508
+ await self._wait_for(lambda: self._data == b"")
509
+ if self._sender_closed:
510
+ raise _core.ClosedResourceError
511
+ if self._data and self._receiver_closed:
512
+ raise _core.BrokenResourceError
513
+
514
+ async def wait_send_all_might_not_block(self) -> None:
515
+ with self._send_conflict_detector:
516
+ if self._sender_closed:
517
+ raise _core.ClosedResourceError
518
+ if self._receiver_closed:
519
+ await _core.checkpoint()
520
+ return
521
+ await self._wait_for(lambda: self._receiver_waiting)
522
+ if self._sender_closed:
523
+ raise _core.ClosedResourceError
524
+
525
+ async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:
526
+ with self._receive_conflict_detector:
527
+ # Argument validation
528
+ if max_bytes is not None:
529
+ max_bytes = operator.index(max_bytes)
530
+ if max_bytes < 1:
531
+ raise ValueError("max_bytes must be >= 1")
532
+ # State validation
533
+ if self._receiver_closed:
534
+ raise _core.ClosedResourceError
535
+ # Wake wait_send_all_might_not_block and wait for data
536
+ self._receiver_waiting = True
537
+ self._something_happened()
538
+ try:
539
+ await self._wait_for(lambda: self._data != b"")
540
+ finally:
541
+ self._receiver_waiting = False
542
+ if self._receiver_closed:
543
+ raise _core.ClosedResourceError
544
+ # Get data, possibly waking send_all
545
+ if self._data:
546
+ # Neat trick: if max_bytes is None, then obj[:max_bytes] is
547
+ # the same as obj[:].
548
+ got = self._data[:max_bytes]
549
+ del self._data[:max_bytes]
550
+ self._something_happened()
551
+ return got
552
+ else:
553
+ assert self._sender_closed
554
+ return b""
555
+
556
+
557
+ class _LockstepSendStream(SendStream):
558
+ def __init__(self, lbq: _LockstepByteQueue) -> None:
559
+ self._lbq = lbq
560
+
561
+ def close(self) -> None:
562
+ self._lbq.close_sender()
563
+
564
+ async def aclose(self) -> None:
565
+ self.close()
566
+ await _core.checkpoint()
567
+
568
+ async def send_all(self, data: bytes | bytearray | memoryview) -> None:
569
+ await self._lbq.send_all(data)
570
+
571
+ async def wait_send_all_might_not_block(self) -> None:
572
+ await self._lbq.wait_send_all_might_not_block()
573
+
574
+
575
+ class _LockstepReceiveStream(ReceiveStream):
576
+ def __init__(self, lbq: _LockstepByteQueue) -> None:
577
+ self._lbq = lbq
578
+
579
+ def close(self) -> None:
580
+ self._lbq.close_receiver()
581
+
582
+ async def aclose(self) -> None:
583
+ self.close()
584
+ await _core.checkpoint()
585
+
586
+ async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:
587
+ return await self._lbq.receive_some(max_bytes)
588
+
589
+
590
+ def lockstep_stream_one_way_pair() -> tuple[SendStream, ReceiveStream]:
591
+ """Create a connected, pure Python, unidirectional stream where data flows
592
+ in lockstep.
593
+
594
+ Returns:
595
+ A tuple
596
+ (:class:`~trio.abc.SendStream`, :class:`~trio.abc.ReceiveStream`).
597
+
598
+ This stream has *absolutely no* buffering. Each call to
599
+ :meth:`~trio.abc.SendStream.send_all` will block until all the given data
600
+ has been returned by a call to
601
+ :meth:`~trio.abc.ReceiveStream.receive_some`.
602
+
603
+ This can be useful for testing flow control mechanisms in an extreme case,
604
+ or for setting up "clogged" streams to use with
605
+ :func:`check_one_way_stream` and friends.
606
+
607
+ In addition to fulfilling the :class:`~trio.abc.SendStream` and
608
+ :class:`~trio.abc.ReceiveStream` interfaces, the return objects
609
+ also have a synchronous ``close`` method.
610
+
611
+ """
612
+
613
+ lbq = _LockstepByteQueue()
614
+ return _LockstepSendStream(lbq), _LockstepReceiveStream(lbq)
615
+
616
+
617
+ def lockstep_stream_pair() -> tuple[
618
+ StapledStream[SendStream, ReceiveStream],
619
+ StapledStream[SendStream, ReceiveStream],
620
+ ]:
621
+ """Create a connected, pure-Python, bidirectional stream where data flows
622
+ in lockstep.
623
+
624
+ Returns:
625
+ A tuple (:class:`~trio.StapledStream`, :class:`~trio.StapledStream`).
626
+
627
+ This is a convenience function that creates two one-way streams using
628
+ :func:`lockstep_stream_one_way_pair`, and then uses
629
+ :class:`~trio.StapledStream` to combine them into a single bidirectional
630
+ stream.
631
+
632
+ """
633
+ return _make_stapled_pair(lockstep_stream_one_way_pair)
@@ -0,0 +1,36 @@
1
+ from .. import socket as tsocket
2
+ from .._highlevel_socket import SocketListener, SocketStream
3
+
4
+
5
+ async def open_stream_to_socket_listener(
6
+ socket_listener: SocketListener,
7
+ ) -> SocketStream:
8
+ """Connect to the given :class:`~trio.SocketListener`.
9
+
10
+ This is particularly useful in tests when you want to let a server pick
11
+ its own port, and then connect to it::
12
+
13
+ listeners = await trio.open_tcp_listeners(0)
14
+ client = await trio.testing.open_stream_to_socket_listener(listeners[0])
15
+
16
+ Args:
17
+ socket_listener (~trio.SocketListener): The
18
+ :class:`~trio.SocketListener` to connect to.
19
+
20
+ Returns:
21
+ SocketStream: a stream connected to the given listener.
22
+
23
+ """
24
+ family = socket_listener.socket.family
25
+ sockaddr = socket_listener.socket.getsockname()
26
+ if family in (tsocket.AF_INET, tsocket.AF_INET6):
27
+ sockaddr = list(sockaddr)
28
+ if sockaddr[0] == "0.0.0.0":
29
+ sockaddr[0] = "127.0.0.1"
30
+ if sockaddr[0] == "::":
31
+ sockaddr[0] = "::1"
32
+ sockaddr = tuple(sockaddr)
33
+
34
+ sock = tsocket.socket(family=family)
35
+ await sock.connect(sockaddr)
36
+ return SocketStream(sock)