@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,522 @@
1
+ from __future__ import annotations
2
+
3
+ import random
4
+ import select
5
+ import socket as stdlib_socket
6
+ import sys
7
+ from collections.abc import Awaitable, Callable
8
+ from contextlib import suppress
9
+ from typing import TYPE_CHECKING, TypeVar
10
+
11
+ import pytest
12
+
13
+ import trio
14
+
15
+ from ... import _core
16
+ from ...testing import assert_checkpoints, wait_all_tasks_blocked
17
+
18
+ # Cross-platform tests for IO handling
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Generator
22
+
23
+ from typing_extensions import ParamSpec
24
+
25
+ ArgsT = ParamSpec("ArgsT")
26
+
27
+
28
+ def fill_socket(sock: stdlib_socket.socket) -> None:
29
+ try:
30
+ while True:
31
+ sock.send(b"x" * 65536)
32
+ except BlockingIOError:
33
+ pass
34
+
35
+
36
+ def drain_socket(sock: stdlib_socket.socket) -> None:
37
+ try:
38
+ while True:
39
+ sock.recv(65536)
40
+ except BlockingIOError:
41
+ pass
42
+
43
+
44
+ WaitSocket = Callable[[stdlib_socket.socket], Awaitable[object]]
45
+ SocketPair = tuple[stdlib_socket.socket, stdlib_socket.socket]
46
+ RetT = TypeVar("RetT")
47
+
48
+
49
+ @pytest.fixture
50
+ def socketpair() -> Generator[SocketPair, None, None]:
51
+ pair = stdlib_socket.socketpair()
52
+ for sock in pair:
53
+ sock.setblocking(False)
54
+ yield pair
55
+ for sock in pair:
56
+ sock.close()
57
+
58
+
59
+ def also_using_fileno(
60
+ fn: Callable[[stdlib_socket.socket | int], RetT],
61
+ ) -> list[Callable[[stdlib_socket.socket], RetT]]:
62
+ def fileno_wrapper(fileobj: stdlib_socket.socket) -> RetT:
63
+ return fn(fileobj.fileno())
64
+
65
+ name = f"<{fn.__name__} on fileno>"
66
+ fileno_wrapper.__name__ = fileno_wrapper.__qualname__ = name
67
+ return [fn, fileno_wrapper]
68
+
69
+
70
+ # Decorators that feed in different settings for wait_readable / wait_writable
71
+ # / notify_closing.
72
+ # Note that if you use all three decorators on the same test, it will run all
73
+ # N**3 *combinations*
74
+ read_socket_test = pytest.mark.parametrize(
75
+ "wait_readable",
76
+ also_using_fileno(trio.lowlevel.wait_readable),
77
+ ids=lambda fn: fn.__name__,
78
+ )
79
+ write_socket_test = pytest.mark.parametrize(
80
+ "wait_writable",
81
+ also_using_fileno(trio.lowlevel.wait_writable),
82
+ ids=lambda fn: fn.__name__,
83
+ )
84
+ notify_closing_test = pytest.mark.parametrize(
85
+ "notify_closing",
86
+ also_using_fileno(trio.lowlevel.notify_closing),
87
+ ids=lambda fn: fn.__name__,
88
+ )
89
+
90
+
91
+ # XX These tests are all a bit dicey because they can't distinguish between
92
+ # wait_on_{read,writ}able blocking the way it should, versus blocking
93
+ # momentarily and then immediately resuming.
94
+ @read_socket_test
95
+ @write_socket_test
96
+ async def test_wait_basic(
97
+ socketpair: SocketPair,
98
+ wait_readable: WaitSocket,
99
+ wait_writable: WaitSocket,
100
+ ) -> None:
101
+ a, b = socketpair
102
+
103
+ # They start out writable()
104
+ with assert_checkpoints():
105
+ await wait_writable(a)
106
+
107
+ # But readable() blocks until data arrives
108
+ record = []
109
+
110
+ async def block_on_read() -> None:
111
+ try:
112
+ with assert_checkpoints():
113
+ await wait_readable(a)
114
+ except _core.Cancelled:
115
+ record.append("cancelled")
116
+ else:
117
+ record.append("readable")
118
+ assert a.recv(10) == b"x"
119
+
120
+ async with _core.open_nursery() as nursery:
121
+ nursery.start_soon(block_on_read)
122
+ await wait_all_tasks_blocked()
123
+ assert record == []
124
+ b.send(b"x")
125
+
126
+ fill_socket(a)
127
+
128
+ # Now writable will block, but readable won't
129
+ with assert_checkpoints():
130
+ await wait_readable(b)
131
+ record = []
132
+
133
+ async def block_on_write() -> None:
134
+ try:
135
+ with assert_checkpoints():
136
+ await wait_writable(a)
137
+ except _core.Cancelled:
138
+ record.append("cancelled")
139
+ else:
140
+ record.append("writable")
141
+
142
+ async with _core.open_nursery() as nursery:
143
+ nursery.start_soon(block_on_write)
144
+ await wait_all_tasks_blocked()
145
+ assert record == []
146
+ drain_socket(b)
147
+
148
+ # check cancellation
149
+ record = []
150
+ async with _core.open_nursery() as nursery:
151
+ nursery.start_soon(block_on_read)
152
+ await wait_all_tasks_blocked()
153
+ nursery.cancel_scope.cancel()
154
+ assert record == ["cancelled"]
155
+
156
+ fill_socket(a)
157
+ record = []
158
+ async with _core.open_nursery() as nursery:
159
+ nursery.start_soon(block_on_write)
160
+ await wait_all_tasks_blocked()
161
+ nursery.cancel_scope.cancel()
162
+ assert record == ["cancelled"]
163
+
164
+
165
+ @read_socket_test
166
+ async def test_double_read(socketpair: SocketPair, wait_readable: WaitSocket) -> None:
167
+ a, _b = socketpair
168
+
169
+ # You can't have two tasks trying to read from a socket at the same time
170
+ async with _core.open_nursery() as nursery:
171
+ nursery.start_soon(wait_readable, a)
172
+ await wait_all_tasks_blocked()
173
+ with pytest.raises(_core.BusyResourceError):
174
+ await wait_readable(a)
175
+ nursery.cancel_scope.cancel()
176
+
177
+
178
+ @write_socket_test
179
+ async def test_double_write(socketpair: SocketPair, wait_writable: WaitSocket) -> None:
180
+ a, _b = socketpair
181
+
182
+ # You can't have two tasks trying to write to a socket at the same time
183
+ fill_socket(a)
184
+ async with _core.open_nursery() as nursery:
185
+ nursery.start_soon(wait_writable, a)
186
+ await wait_all_tasks_blocked()
187
+ with pytest.raises(_core.BusyResourceError):
188
+ await wait_writable(a)
189
+ nursery.cancel_scope.cancel()
190
+
191
+
192
+ @read_socket_test
193
+ @write_socket_test
194
+ @notify_closing_test
195
+ async def test_interrupted_by_close(
196
+ socketpair: SocketPair,
197
+ wait_readable: WaitSocket,
198
+ wait_writable: WaitSocket,
199
+ notify_closing: Callable[[stdlib_socket.socket], object],
200
+ ) -> None:
201
+ a, _b = socketpair
202
+
203
+ async def reader() -> None:
204
+ with pytest.raises(_core.ClosedResourceError):
205
+ await wait_readable(a)
206
+
207
+ async def writer() -> None:
208
+ with pytest.raises(_core.ClosedResourceError):
209
+ await wait_writable(a)
210
+
211
+ fill_socket(a)
212
+
213
+ async with _core.open_nursery() as nursery:
214
+ nursery.start_soon(reader)
215
+ nursery.start_soon(writer)
216
+ await wait_all_tasks_blocked()
217
+ notify_closing(a)
218
+
219
+
220
+ @read_socket_test
221
+ @write_socket_test
222
+ async def test_socket_simultaneous_read_write(
223
+ socketpair: SocketPair,
224
+ wait_readable: WaitSocket,
225
+ wait_writable: WaitSocket,
226
+ ) -> None:
227
+ record: list[str] = []
228
+
229
+ async def r_task(sock: stdlib_socket.socket) -> None:
230
+ await wait_readable(sock)
231
+ record.append("r_task")
232
+
233
+ async def w_task(sock: stdlib_socket.socket) -> None:
234
+ await wait_writable(sock)
235
+ record.append("w_task")
236
+
237
+ a, b = socketpair
238
+ fill_socket(a)
239
+ async with _core.open_nursery() as nursery:
240
+ nursery.start_soon(r_task, a)
241
+ nursery.start_soon(w_task, a)
242
+ await wait_all_tasks_blocked()
243
+ assert record == []
244
+ b.send(b"x")
245
+ await wait_all_tasks_blocked()
246
+ assert record == ["r_task"]
247
+ drain_socket(b)
248
+ await wait_all_tasks_blocked()
249
+ assert record == ["r_task", "w_task"]
250
+
251
+
252
+ @read_socket_test
253
+ @write_socket_test
254
+ async def test_socket_actual_streaming(
255
+ socketpair: SocketPair,
256
+ wait_readable: WaitSocket,
257
+ wait_writable: WaitSocket,
258
+ ) -> None:
259
+ a, b = socketpair
260
+
261
+ # Use a small send buffer on one of the sockets to increase the chance of
262
+ # getting partial writes
263
+ a.setsockopt(stdlib_socket.SOL_SOCKET, stdlib_socket.SO_SNDBUF, 10000)
264
+
265
+ N = 1000000 # 1 megabyte
266
+ MAX_CHUNK = 65536
267
+
268
+ results: dict[str, int] = {}
269
+
270
+ async def sender(sock: stdlib_socket.socket, seed: int, key: str) -> None:
271
+ r = random.Random(seed)
272
+ sent = 0
273
+ while sent < N:
274
+ print("sent", sent)
275
+ chunk = bytearray(r.randrange(MAX_CHUNK))
276
+ while chunk:
277
+ with assert_checkpoints():
278
+ await wait_writable(sock)
279
+ this_chunk_size = sock.send(chunk)
280
+ sent += this_chunk_size
281
+ del chunk[:this_chunk_size]
282
+ sock.shutdown(stdlib_socket.SHUT_WR)
283
+ results[key] = sent
284
+
285
+ async def receiver(sock: stdlib_socket.socket, key: str) -> None:
286
+ received = 0
287
+ while True:
288
+ print("received", received)
289
+ with assert_checkpoints():
290
+ await wait_readable(sock)
291
+ this_chunk_size = len(sock.recv(MAX_CHUNK))
292
+ if not this_chunk_size:
293
+ break
294
+ received += this_chunk_size
295
+ results[key] = received
296
+
297
+ async with _core.open_nursery() as nursery:
298
+ nursery.start_soon(sender, a, 0, "send_a")
299
+ nursery.start_soon(sender, b, 1, "send_b")
300
+ nursery.start_soon(receiver, a, "recv_a")
301
+ nursery.start_soon(receiver, b, "recv_b")
302
+
303
+ assert results["send_a"] == results["recv_b"]
304
+ assert results["send_b"] == results["recv_a"]
305
+
306
+
307
+ async def test_notify_closing_on_invalid_object() -> None:
308
+ # It should either be a no-op (generally on Unix, where we don't know
309
+ # which fds are valid), or an OSError (on Windows, where we currently only
310
+ # support sockets, so we have to do some validation to figure out whether
311
+ # it's a socket or a regular handle).
312
+ got_oserror = False
313
+ got_no_error = False
314
+ try:
315
+ trio.lowlevel.notify_closing(-1)
316
+ except OSError:
317
+ got_oserror = True
318
+ else:
319
+ got_no_error = True
320
+ assert got_oserror or got_no_error
321
+
322
+
323
+ async def test_wait_on_invalid_object() -> None:
324
+ # We definitely want to raise an error everywhere if you pass in an
325
+ # invalid fd to wait_*
326
+ for wait in [trio.lowlevel.wait_readable, trio.lowlevel.wait_writable]:
327
+ with stdlib_socket.socket() as s:
328
+ fileno = s.fileno()
329
+ # We just closed the socket and don't do anything else in between, so
330
+ # we can be confident that the fileno hasn't be reassigned.
331
+ with pytest.raises(
332
+ OSError,
333
+ match=r"^\[\w+ \d+] (Bad file descriptor|An operation was attempted on something that is not a socket)$",
334
+ ):
335
+ await wait(fileno)
336
+
337
+
338
+ async def test_io_manager_statistics() -> None:
339
+ def check(*, expected_readers: int, expected_writers: int) -> None:
340
+ statistics = _core.current_statistics()
341
+ print(statistics)
342
+ iostats = statistics.io_statistics
343
+ if iostats.backend == "epoll" or iostats.backend == "windows":
344
+ assert iostats.tasks_waiting_read == expected_readers
345
+ assert iostats.tasks_waiting_write == expected_writers
346
+ else:
347
+ assert iostats.backend == "kqueue"
348
+ assert iostats.monitors == 0
349
+ assert iostats.tasks_waiting == expected_readers + expected_writers
350
+
351
+ a1, b1 = stdlib_socket.socketpair()
352
+ a2, b2 = stdlib_socket.socketpair()
353
+ a3, b3 = stdlib_socket.socketpair()
354
+ for sock in [a1, b1, a2, b2, a3, b3]:
355
+ sock.setblocking(False)
356
+ with a1, b1, a2, b2, a3, b3:
357
+ # let the call_soon_task settle down
358
+ await wait_all_tasks_blocked()
359
+
360
+ # 1 for call_soon_task
361
+ check(expected_readers=1, expected_writers=0)
362
+
363
+ # We want:
364
+ # - one socket with a writer blocked
365
+ # - two sockets with a reader blocked
366
+ # - a socket with both blocked
367
+ fill_socket(a1)
368
+ fill_socket(a3)
369
+ async with _core.open_nursery() as nursery:
370
+ nursery.start_soon(_core.wait_writable, a1)
371
+ nursery.start_soon(_core.wait_readable, a2)
372
+ nursery.start_soon(_core.wait_readable, b2)
373
+ nursery.start_soon(_core.wait_writable, a3)
374
+ nursery.start_soon(_core.wait_readable, a3)
375
+
376
+ await wait_all_tasks_blocked()
377
+
378
+ # +1 for call_soon_task
379
+ check(expected_readers=3 + 1, expected_writers=2)
380
+
381
+ nursery.cancel_scope.cancel()
382
+
383
+ # 1 for call_soon_task
384
+ check(expected_readers=1, expected_writers=0)
385
+
386
+
387
+ @pytest.mark.filterwarnings("ignore:.*UnboundedQueue:trio.TrioDeprecationWarning")
388
+ async def test_io_manager_kqueue_monitors_statistics() -> None:
389
+ def check(
390
+ *,
391
+ expected_monitors: int,
392
+ expected_readers: int,
393
+ expected_writers: int,
394
+ ) -> None:
395
+ statistics = _core.current_statistics()
396
+ print(statistics)
397
+ iostats = statistics.io_statistics
398
+ assert iostats.backend == "kqueue"
399
+ assert iostats.monitors == expected_monitors
400
+ assert iostats.tasks_waiting == expected_readers + expected_writers
401
+
402
+ a1, b1 = stdlib_socket.socketpair()
403
+ for sock in [a1, b1]:
404
+ sock.setblocking(False)
405
+
406
+ with a1, b1:
407
+ # let the call_soon_task settle down
408
+ await wait_all_tasks_blocked()
409
+
410
+ if sys.platform != "win32" and sys.platform != "linux":
411
+ # 1 for call_soon_task
412
+ check(expected_monitors=0, expected_readers=1, expected_writers=0)
413
+
414
+ with _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):
415
+ with (
416
+ pytest.raises(_core.BusyResourceError),
417
+ _core.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ),
418
+ ):
419
+ pass # pragma: no cover
420
+ check(expected_monitors=1, expected_readers=1, expected_writers=0)
421
+
422
+ check(expected_monitors=0, expected_readers=1, expected_writers=0)
423
+
424
+
425
+ async def test_can_survive_unnotified_close() -> None:
426
+ # An "unnotified" close is when the user closes an fd/socket/handle
427
+ # directly, without calling notify_closing first. This should never happen
428
+ # -- users should call notify_closing before closing things. But, just in
429
+ # case they don't, we would still like to avoid exploding.
430
+ #
431
+ # Acceptable behaviors:
432
+ # - wait_* never return, but can be cancelled cleanly
433
+ # - wait_* exit cleanly
434
+ # - wait_* raise an OSError
435
+ #
436
+ # Not acceptable:
437
+ # - getting stuck in an uncancellable state
438
+ # - TrioInternalError blowing up the whole run
439
+ #
440
+ # This test exercises some tricky "unnotified close" scenarios, to make
441
+ # sure we get the "acceptable" behaviors.
442
+
443
+ async def allow_OSError(
444
+ async_func: Callable[ArgsT, Awaitable[object]],
445
+ *args: ArgsT.args,
446
+ **kwargs: ArgsT.kwargs,
447
+ ) -> None:
448
+ with suppress(OSError):
449
+ await async_func(*args, **kwargs)
450
+
451
+ with stdlib_socket.socket() as s:
452
+ async with trio.open_nursery() as nursery:
453
+ nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, s)
454
+ await wait_all_tasks_blocked()
455
+ s.close()
456
+ await wait_all_tasks_blocked()
457
+ nursery.cancel_scope.cancel()
458
+
459
+ # We hit different paths on Windows depending on whether we close the last
460
+ # handle to the object (which produces a LOCAL_CLOSE notification and
461
+ # wakes up wait_readable), or only close one of the handles (which leaves
462
+ # wait_readable pending until cancelled).
463
+ with stdlib_socket.socket() as s, s.dup() as s2: # noqa: F841
464
+ async with trio.open_nursery() as nursery:
465
+ nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, s)
466
+ await wait_all_tasks_blocked()
467
+ s.close()
468
+ await wait_all_tasks_blocked()
469
+ nursery.cancel_scope.cancel()
470
+
471
+ # A more elaborate case, with two tasks waiting. On windows and epoll,
472
+ # the two tasks get muxed together onto a single underlying wait
473
+ # operation. So when they're cancelled, there's a brief moment where one
474
+ # of the tasks is cancelled but the other isn't, so we try to re-issue the
475
+ # underlying wait operation. But here, the handle we were going to use to
476
+ # do that has been pulled out from under our feet... so test that we can
477
+ # survive this.
478
+ a, b = stdlib_socket.socketpair()
479
+ with a, b, a.dup() as a2:
480
+ a.setblocking(False)
481
+ b.setblocking(False)
482
+ fill_socket(a)
483
+ async with trio.open_nursery() as nursery:
484
+ nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, a)
485
+ nursery.start_soon(allow_OSError, trio.lowlevel.wait_writable, a)
486
+ await wait_all_tasks_blocked()
487
+ a.close()
488
+ nursery.cancel_scope.cancel()
489
+
490
+ # A similar case, but now the single-task-wakeup happens due to I/O
491
+ # arriving, not a cancellation, so the operation gets re-issued from
492
+ # handle_io context rather than abort context.
493
+ a, b = stdlib_socket.socketpair()
494
+ with a, b, a.dup() as a2:
495
+ print(f"a={a.fileno()}, b={b.fileno()}, a2={a2.fileno()}")
496
+ a.setblocking(False)
497
+ b.setblocking(False)
498
+ fill_socket(a)
499
+ e = trio.Event()
500
+
501
+ # We want to wait for the kernel to process the wakeup on 'a', if any.
502
+ # But depending on the platform, we might not get a wakeup on 'a'. So
503
+ # we put one task to sleep waiting on 'a', and we put a second task to
504
+ # sleep waiting on 'a2', with the idea that the 'a2' notification will
505
+ # definitely arrive, and when it does then we can assume that whatever
506
+ # notification was going to arrive for 'a' has also arrived.
507
+ async def wait_readable_a2_then_set() -> None:
508
+ await trio.lowlevel.wait_readable(a2)
509
+ e.set()
510
+
511
+ async with trio.open_nursery() as nursery:
512
+ nursery.start_soon(allow_OSError, trio.lowlevel.wait_readable, a)
513
+ nursery.start_soon(allow_OSError, trio.lowlevel.wait_writable, a)
514
+ nursery.start_soon(wait_readable_a2_then_set)
515
+ await wait_all_tasks_blocked()
516
+ a.close()
517
+ b.send(b"x")
518
+ # Make sure that the wakeup has been received and everything has
519
+ # settled before cancelling the wait_writable.
520
+ await e.wait()
521
+ await wait_all_tasks_blocked()
522
+ nursery.cancel_scope.cancel()