@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,86 @@
1
+ import os
2
+ import socket
3
+ import sys
4
+ import tempfile
5
+ from typing import TYPE_CHECKING
6
+
7
+ import pytest
8
+
9
+ from trio import Path, open_unix_socket
10
+ from trio._highlevel_open_unix_stream import close_on_error
11
+
12
+ assert not TYPE_CHECKING or sys.platform != "win32"
13
+
14
+ skip_if_not_unix = pytest.mark.skipif(
15
+ not hasattr(socket, "AF_UNIX"),
16
+ reason="Needs unix socket support",
17
+ )
18
+
19
+
20
+ @skip_if_not_unix
21
+ def test_close_on_error() -> None:
22
+ class CloseMe:
23
+ closed = False
24
+
25
+ def close(self) -> None:
26
+ self.closed = True
27
+
28
+ with close_on_error(CloseMe()) as c:
29
+ pass
30
+ assert not c.closed
31
+
32
+ with pytest.raises(RuntimeError):
33
+ with close_on_error(CloseMe()) as c:
34
+ raise RuntimeError
35
+ assert c.closed
36
+
37
+
38
+ @skip_if_not_unix
39
+ @pytest.mark.parametrize("filename", [4, 4.5])
40
+ async def test_open_with_bad_filename_type(filename: float) -> None:
41
+ with pytest.raises(TypeError):
42
+ await open_unix_socket(filename) # type: ignore[arg-type]
43
+
44
+
45
+ @skip_if_not_unix
46
+ async def test_open_bad_socket() -> None:
47
+ # mktemp is marked as insecure, but that's okay, we don't want the file to
48
+ # exist
49
+ name = tempfile.mktemp()
50
+ with pytest.raises(FileNotFoundError):
51
+ await open_unix_socket(name)
52
+
53
+
54
+ @skip_if_not_unix
55
+ async def test_open_unix_socket() -> None:
56
+ for name_type in [Path, str]:
57
+ name = tempfile.mktemp()
58
+ serv_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
59
+ with serv_sock:
60
+ serv_sock.bind(name)
61
+ try:
62
+ serv_sock.listen(1)
63
+
64
+ # The actual function we're testing
65
+ unix_socket = await open_unix_socket(name_type(name))
66
+
67
+ async with unix_socket:
68
+ client, _ = serv_sock.accept()
69
+ with client:
70
+ await unix_socket.send_all(b"test")
71
+ assert client.recv(2048) == b"test"
72
+
73
+ client.sendall(b"response")
74
+ received = await unix_socket.receive_some(2048)
75
+ assert received == b"response"
76
+ finally:
77
+ os.unlink(name)
78
+
79
+
80
+ @pytest.mark.skipif(hasattr(socket, "AF_UNIX"), reason="Test for non-unix platforms")
81
+ async def test_error_on_no_unix() -> None:
82
+ with pytest.raises(
83
+ RuntimeError,
84
+ match=r"^Unix sockets are not supported on this platform$",
85
+ ):
86
+ await open_unix_socket("")
@@ -0,0 +1,186 @@
1
+ from __future__ import annotations
2
+
3
+ import errno
4
+ from functools import partial
5
+ from typing import TYPE_CHECKING, NoReturn, cast
6
+
7
+ import attrs
8
+ import pytest
9
+
10
+ import trio
11
+ from trio import Nursery, StapledStream, TaskStatus
12
+ from trio.testing import (
13
+ MemoryReceiveStream,
14
+ MemorySendStream,
15
+ MockClock,
16
+ memory_stream_pair,
17
+ wait_all_tasks_blocked,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Awaitable, Callable
22
+
23
+ from trio._channel import MemoryReceiveChannel, MemorySendChannel
24
+ from trio.abc import Stream
25
+
26
+ # types are somewhat tentative - I just bruteforced them until I got something that didn't
27
+ # give errors
28
+ StapledMemoryStream = StapledStream[MemorySendStream, MemoryReceiveStream]
29
+
30
+
31
+ @attrs.define(eq=False, slots=False)
32
+ class MemoryListener(trio.abc.Listener[StapledMemoryStream]):
33
+ closed: bool = False
34
+ accepted_streams: list[trio.abc.Stream] = attrs.Factory(list)
35
+ queued_streams: tuple[
36
+ MemorySendChannel[StapledMemoryStream],
37
+ MemoryReceiveChannel[StapledMemoryStream],
38
+ ] = attrs.Factory(lambda: trio.open_memory_channel[StapledMemoryStream](1))
39
+ accept_hook: Callable[[], Awaitable[object]] | None = None
40
+
41
+ async def connect(self) -> StapledMemoryStream:
42
+ assert not self.closed
43
+ client, server = memory_stream_pair()
44
+ await self.queued_streams[0].send(server)
45
+ return client
46
+
47
+ async def accept(self) -> StapledMemoryStream:
48
+ await trio.lowlevel.checkpoint()
49
+ assert not self.closed
50
+ if self.accept_hook is not None:
51
+ await self.accept_hook()
52
+ stream = await self.queued_streams[1].receive()
53
+ self.accepted_streams.append(stream)
54
+ return stream
55
+
56
+ async def aclose(self) -> None:
57
+ self.closed = True
58
+ await trio.lowlevel.checkpoint()
59
+
60
+
61
+ async def test_serve_listeners_basic() -> None:
62
+ listeners = [MemoryListener(), MemoryListener()]
63
+
64
+ record = []
65
+
66
+ def close_hook() -> None:
67
+ # Make sure this is a forceful close
68
+ assert trio.current_effective_deadline() == float("-inf")
69
+ record.append("closed")
70
+
71
+ async def handler(stream: StapledMemoryStream) -> None:
72
+ await stream.send_all(b"123")
73
+ assert await stream.receive_some(10) == b"456"
74
+ stream.send_stream.close_hook = close_hook
75
+ stream.receive_stream.close_hook = close_hook
76
+
77
+ async def client(listener: MemoryListener) -> None:
78
+ s = await listener.connect()
79
+ assert await s.receive_some(10) == b"123"
80
+ await s.send_all(b"456")
81
+
82
+ async def do_tests(parent_nursery: Nursery) -> None:
83
+ async with trio.open_nursery() as nursery:
84
+ for listener in listeners:
85
+ for _ in range(3):
86
+ nursery.start_soon(client, listener)
87
+
88
+ await wait_all_tasks_blocked()
89
+
90
+ # verifies that all 6 streams x 2 directions each were closed ok
91
+ assert len(record) == 12
92
+
93
+ parent_nursery.cancel_scope.cancel()
94
+
95
+ async with trio.open_nursery() as nursery:
96
+ value = await nursery.start(
97
+ trio.serve_listeners,
98
+ handler,
99
+ listeners,
100
+ )
101
+ assert isinstance(value, list)
102
+ l2 = cast("list[MemoryListener]", value)
103
+ assert l2 == listeners
104
+ # This is just split into another function because gh-136 isn't
105
+ # implemented yet
106
+ nursery.start_soon(do_tests, nursery)
107
+
108
+ for listener in listeners:
109
+ assert listener.closed
110
+
111
+
112
+ async def test_serve_listeners_accept_unrecognized_error() -> None:
113
+ for error in [KeyError(), OSError(errno.ECONNABORTED, "ECONNABORTED")]:
114
+ listener = MemoryListener()
115
+
116
+ async def raise_error() -> NoReturn:
117
+ raise error # noqa: B023 # Set from loop
118
+
119
+ def check_error(e: BaseException) -> bool:
120
+ return e is error # noqa: B023
121
+
122
+ listener.accept_hook = raise_error
123
+
124
+ with pytest.RaisesGroup(pytest.RaisesExc(check=check_error)):
125
+ await trio.serve_listeners(None, [listener]) # type: ignore[arg-type]
126
+
127
+
128
+ async def test_serve_listeners_accept_capacity_error(
129
+ autojump_clock: MockClock,
130
+ caplog: pytest.LogCaptureFixture,
131
+ ) -> None:
132
+ listener = MemoryListener()
133
+
134
+ async def raise_EMFILE() -> NoReturn:
135
+ raise OSError(errno.EMFILE, "out of file descriptors")
136
+
137
+ listener.accept_hook = raise_EMFILE
138
+
139
+ # It retries every 100 ms, so in 950 ms it will retry at 0, 100, ..., 900
140
+ # = 10 times total
141
+ with trio.move_on_after(0.950):
142
+ await trio.serve_listeners(None, [listener]) # type: ignore[arg-type]
143
+
144
+ assert len(caplog.records) == 10
145
+ for record in caplog.records:
146
+ assert "retrying" in record.msg
147
+ assert record.exc_info is not None
148
+ assert isinstance(record.exc_info[1], OSError)
149
+ assert record.exc_info[1].errno == errno.EMFILE
150
+
151
+
152
+ async def test_serve_listeners_connection_nursery(autojump_clock: MockClock) -> None:
153
+ listener = MemoryListener()
154
+
155
+ async def handler(stream: Stream) -> None:
156
+ await trio.sleep(1)
157
+
158
+ class Done(Exception):
159
+ pass
160
+
161
+ async def connection_watcher(
162
+ *,
163
+ task_status: TaskStatus[Nursery] = trio.TASK_STATUS_IGNORED,
164
+ ) -> NoReturn:
165
+ async with trio.open_nursery() as nursery:
166
+ task_status.started(nursery)
167
+ await wait_all_tasks_blocked()
168
+ assert len(nursery.child_tasks) == 10
169
+ raise Done
170
+
171
+ # the exception is wrapped twice because we open two nested nurseries
172
+ with pytest.RaisesGroup(pytest.RaisesGroup(Done)):
173
+ async with trio.open_nursery() as nursery:
174
+ value = await nursery.start(connection_watcher)
175
+ assert isinstance(value, trio.Nursery)
176
+ handler_nursery: trio.Nursery = value
177
+ await nursery.start(
178
+ partial(
179
+ trio.serve_listeners,
180
+ handler,
181
+ [listener],
182
+ handler_nursery=handler_nursery,
183
+ ),
184
+ )
185
+ for _ in range(10):
186
+ nursery.start_soon(listener.connect)
@@ -0,0 +1,336 @@
1
+ from __future__ import annotations
2
+
3
+ import errno
4
+ import socket as stdlib_socket
5
+ import sys
6
+ from typing import TYPE_CHECKING
7
+
8
+ import pytest
9
+
10
+ from .. import _core, socket as tsocket
11
+ from .._highlevel_socket import *
12
+ from ..testing import (
13
+ assert_checkpoints,
14
+ check_half_closeable_stream,
15
+ wait_all_tasks_blocked,
16
+ )
17
+ from .test_socket import setsockopt_tests
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Sequence
21
+
22
+
23
+ async def test_SocketStream_basics() -> None:
24
+ # stdlib socket bad (even if connected)
25
+ stdlib_a, stdlib_b = stdlib_socket.socketpair()
26
+ with stdlib_a, stdlib_b:
27
+ with pytest.raises(TypeError):
28
+ SocketStream(stdlib_a) # type: ignore[arg-type]
29
+
30
+ # DGRAM socket bad
31
+ with tsocket.socket(type=tsocket.SOCK_DGRAM) as sock:
32
+ with pytest.raises(
33
+ ValueError,
34
+ match=r"^SocketStream requires a SOCK_STREAM socket$",
35
+ ):
36
+ # TODO: does not raise an error?
37
+ SocketStream(sock)
38
+
39
+ a, b = tsocket.socketpair()
40
+ with a, b:
41
+ s = SocketStream(a)
42
+ assert s.socket is a
43
+
44
+ # Use a real, connected socket to test socket options, because
45
+ # socketpair() might give us a unix socket that doesn't support any of
46
+ # these options
47
+ with tsocket.socket() as listen_sock:
48
+ await listen_sock.bind(("127.0.0.1", 0))
49
+ listen_sock.listen(1)
50
+ with tsocket.socket() as client_sock:
51
+ await client_sock.connect(listen_sock.getsockname())
52
+
53
+ s = SocketStream(client_sock)
54
+
55
+ # TCP_NODELAY enabled by default
56
+ assert s.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)
57
+ # We can disable it though
58
+ s.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False)
59
+ assert not s.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)
60
+
61
+ res = s.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, 1)
62
+ assert isinstance(res, bytes)
63
+
64
+ setsockopt_tests(s)
65
+
66
+
67
+ async def test_SocketStream_send_all() -> None:
68
+ BIG = 10000000
69
+
70
+ a_sock, b_sock = tsocket.socketpair()
71
+ with a_sock, b_sock:
72
+ a = SocketStream(a_sock)
73
+ b = SocketStream(b_sock)
74
+
75
+ # Check a send_all that has to be split into multiple parts (on most
76
+ # platforms... on Windows every send() either succeeds or fails as a
77
+ # whole)
78
+ async def sender() -> None:
79
+ data = bytearray(BIG)
80
+ await a.send_all(data)
81
+ # send_all uses memoryviews internally, which temporarily "lock"
82
+ # the object they view. If it doesn't clean them up properly, then
83
+ # some bytearray operations might raise an error afterwards, which
84
+ # would be a pretty weird and annoying side-effect to spring on
85
+ # users. So test that this doesn't happen, by forcing the
86
+ # bytearray's underlying buffer to be realloc'ed:
87
+ data += bytes(BIG)
88
+ # (Note: the above line of code doesn't do a very good job at
89
+ # testing anything, because:
90
+ # - on CPython, the refcount GC generally cleans up memoryviews
91
+ # for us even if we're sloppy.
92
+ # - on PyPy3, at least as of 5.7.0, the memoryview code and the
93
+ # bytearray code conspire so that resizing never fails – if
94
+ # resizing forces the bytearray's internal buffer to move, then
95
+ # all memoryview references are automagically updated (!!).
96
+ # See:
97
+ # https://gist.github.com/njsmith/0ffd38ec05ad8e34004f34a7dc492227
98
+ # But I'm leaving the test here in hopes that if this ever changes
99
+ # and we break our implementation of send_all, then we'll get some
100
+ # early warning...)
101
+
102
+ async def receiver() -> None:
103
+ # Make sure the sender fills up the kernel buffers and blocks
104
+ await wait_all_tasks_blocked()
105
+ nbytes = 0
106
+ while nbytes < BIG:
107
+ nbytes += len(await b.receive_some(BIG))
108
+ assert nbytes == BIG
109
+
110
+ async with _core.open_nursery() as nursery:
111
+ nursery.start_soon(sender)
112
+ nursery.start_soon(receiver)
113
+
114
+ # We know that we received BIG bytes of NULs so far. Make sure that
115
+ # was all the data in there.
116
+ await a.send_all(b"e")
117
+ assert await b.receive_some(10) == b"e"
118
+ await a.send_eof()
119
+ assert await b.receive_some(10) == b""
120
+
121
+
122
+ async def fill_stream(s: SocketStream) -> None:
123
+ async def sender() -> None:
124
+ while True:
125
+ await s.send_all(b"x" * 10000)
126
+
127
+ async def waiter(nursery: _core.Nursery) -> None:
128
+ await wait_all_tasks_blocked()
129
+ nursery.cancel_scope.cancel()
130
+
131
+ async with _core.open_nursery() as nursery:
132
+ nursery.start_soon(sender)
133
+ nursery.start_soon(waiter, nursery)
134
+
135
+
136
+ async def test_SocketStream_generic() -> None:
137
+ async def stream_maker() -> tuple[
138
+ SocketStream,
139
+ SocketStream,
140
+ ]:
141
+ left, right = tsocket.socketpair()
142
+ return SocketStream(left), SocketStream(right)
143
+
144
+ async def clogged_stream_maker() -> tuple[SocketStream, SocketStream]:
145
+ left, right = await stream_maker()
146
+ await fill_stream(left)
147
+ await fill_stream(right)
148
+ return left, right
149
+
150
+ await check_half_closeable_stream(stream_maker, clogged_stream_maker)
151
+
152
+
153
+ async def test_SocketListener() -> None:
154
+ # Not a Trio socket
155
+ with stdlib_socket.socket() as s:
156
+ s.bind(("127.0.0.1", 0))
157
+ s.listen(10)
158
+ with pytest.raises(TypeError):
159
+ SocketListener(s) # type: ignore[arg-type]
160
+
161
+ # Not a SOCK_STREAM
162
+ with tsocket.socket(type=tsocket.SOCK_DGRAM) as s:
163
+ await s.bind(("127.0.0.1", 0))
164
+ with pytest.raises(
165
+ ValueError,
166
+ match=r"^SocketListener requires a SOCK_STREAM socket$",
167
+ ) as excinfo:
168
+ SocketListener(s)
169
+ excinfo.match(r".*SOCK_STREAM")
170
+
171
+ # Didn't call .listen()
172
+ # macOS has no way to check for this, so skip testing it there.
173
+ if sys.platform != "darwin":
174
+ with tsocket.socket() as s:
175
+ await s.bind(("127.0.0.1", 0))
176
+ with pytest.raises(
177
+ ValueError,
178
+ match=r"^SocketListener requires a listening socket$",
179
+ ) as excinfo:
180
+ SocketListener(s)
181
+ excinfo.match(r".*listen")
182
+
183
+ listen_sock = tsocket.socket()
184
+ await listen_sock.bind(("127.0.0.1", 0))
185
+ listen_sock.listen(10)
186
+ listener = SocketListener(listen_sock)
187
+
188
+ assert listener.socket is listen_sock
189
+
190
+ client_sock = tsocket.socket()
191
+ await client_sock.connect(listen_sock.getsockname())
192
+ with assert_checkpoints():
193
+ server_stream = await listener.accept()
194
+ assert isinstance(server_stream, SocketStream)
195
+ assert server_stream.socket.getsockname() == listen_sock.getsockname()
196
+ assert server_stream.socket.getpeername() == client_sock.getsockname()
197
+
198
+ with assert_checkpoints():
199
+ await listener.aclose()
200
+
201
+ with assert_checkpoints():
202
+ await listener.aclose()
203
+
204
+ with assert_checkpoints():
205
+ with pytest.raises(_core.ClosedResourceError):
206
+ await listener.accept()
207
+
208
+ client_sock.close()
209
+ await server_stream.aclose()
210
+
211
+
212
+ async def test_SocketListener_socket_closed_underfoot() -> None:
213
+ listen_sock = tsocket.socket()
214
+ await listen_sock.bind(("127.0.0.1", 0))
215
+ listen_sock.listen(10)
216
+ listener = SocketListener(listen_sock)
217
+
218
+ # Close the socket, not the listener
219
+ listen_sock.close()
220
+
221
+ # SocketListener gives correct error
222
+ with assert_checkpoints():
223
+ with pytest.raises(_core.ClosedResourceError):
224
+ await listener.accept()
225
+
226
+
227
+ async def test_SocketListener_accept_errors() -> None:
228
+ class FakeSocket(tsocket.SocketType):
229
+ def __init__(self, events: Sequence[SocketType | BaseException]) -> None:
230
+ self._events = iter(events)
231
+
232
+ type = tsocket.SOCK_STREAM
233
+
234
+ # Fool the check for SO_ACCEPTCONN in SocketListener.__init__
235
+ @overload
236
+ def getsockopt(self, /, level: int, optname: int) -> int: ...
237
+
238
+ @overload
239
+ def getsockopt( # noqa: F811
240
+ self,
241
+ /,
242
+ level: int,
243
+ optname: int,
244
+ buflen: int,
245
+ ) -> bytes: ...
246
+
247
+ def getsockopt( # noqa: F811
248
+ self,
249
+ /,
250
+ level: int,
251
+ optname: int,
252
+ buflen: int | None = None,
253
+ ) -> int | bytes:
254
+ return True
255
+
256
+ @overload
257
+ def setsockopt(
258
+ self,
259
+ /,
260
+ level: int,
261
+ optname: int,
262
+ value: int | Buffer,
263
+ ) -> None: ...
264
+
265
+ @overload
266
+ def setsockopt( # noqa: F811
267
+ self,
268
+ /,
269
+ level: int,
270
+ optname: int,
271
+ value: None,
272
+ optlen: int,
273
+ ) -> None: ...
274
+
275
+ def setsockopt( # noqa: F811
276
+ self,
277
+ /,
278
+ level: int,
279
+ optname: int,
280
+ value: int | Buffer | None,
281
+ optlen: int | None = None,
282
+ ) -> None:
283
+ pass
284
+
285
+ async def accept(self) -> tuple[SocketType, object]:
286
+ await _core.checkpoint()
287
+ event = next(self._events)
288
+ if isinstance(event, BaseException):
289
+ raise event
290
+ else:
291
+ return event, None
292
+
293
+ fake_server_sock = FakeSocket([])
294
+
295
+ fake_listen_sock = FakeSocket(
296
+ [
297
+ OSError(errno.ECONNABORTED, "Connection aborted"),
298
+ OSError(errno.EPERM, "Permission denied"),
299
+ OSError(errno.EPROTO, "Bad protocol"),
300
+ fake_server_sock,
301
+ OSError(errno.EMFILE, "Out of file descriptors"),
302
+ OSError(errno.EFAULT, "attempt to write to read-only memory"),
303
+ OSError(errno.ENOBUFS, "out of buffers"),
304
+ fake_server_sock,
305
+ ],
306
+ )
307
+
308
+ listener = SocketListener(fake_listen_sock)
309
+
310
+ with assert_checkpoints():
311
+ stream = await listener.accept()
312
+ assert stream.socket is fake_server_sock
313
+
314
+ for code, match in {
315
+ errno.EMFILE: r"\[\w+ \d+\] Out of file descriptors$",
316
+ errno.EFAULT: r"\[\w+ \d+\] attempt to write to read-only memory$",
317
+ errno.ENOBUFS: r"\[\w+ \d+\] out of buffers$",
318
+ }.items():
319
+ with assert_checkpoints():
320
+ with pytest.raises(OSError, match=match) as excinfo:
321
+ await listener.accept()
322
+ assert excinfo.value.errno == code
323
+
324
+ with assert_checkpoints():
325
+ stream = await listener.accept()
326
+ assert stream.socket is fake_server_sock
327
+
328
+
329
+ async def test_socket_stream_works_when_peer_has_already_closed() -> None:
330
+ sock_a, sock_b = tsocket.socketpair()
331
+ with sock_a, sock_b:
332
+ await sock_b.send(b"x")
333
+ sock_b.close()
334
+ stream = SocketStream(sock_a)
335
+ assert await stream.receive_some(1) == b"x"
336
+ assert await stream.receive_some(1) == b""