@ajksunkang-aios/kgraph-linux-x64 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/kgraph-launcher +15 -3
- package/lib/kgraph/scripts/build-bundle.sh +17 -4
- package/lib/site-packages/outcome/__init__.py +20 -0
- package/lib/site-packages/outcome/_impl.py +239 -0
- package/lib/site-packages/outcome/_util.py +33 -0
- package/lib/site-packages/outcome/_version.py +7 -0
- package/lib/site-packages/outcome/py.typed +0 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sniffio/__init__.py +17 -0
- package/lib/site-packages/sniffio/_impl.py +95 -0
- package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
- package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
- package/lib/site-packages/sniffio/_version.py +3 -0
- package/lib/site-packages/sniffio/py.typed +0 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sortedcontainers/__init__.py +74 -0
- package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
- package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
- package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/trio/__init__.py +133 -0
- package/lib/site-packages/trio/__main__.py +3 -0
- package/lib/site-packages/trio/_abc.py +714 -0
- package/lib/site-packages/trio/_channel.py +610 -0
- package/lib/site-packages/trio/_core/__init__.py +94 -0
- package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
- package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
- package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
- package/lib/site-packages/trio/_core/_exceptions.py +169 -0
- package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
- package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
- package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
- package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
- package/lib/site-packages/trio/_core/_generated_run.py +269 -0
- package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
- package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
- package/lib/site-packages/trio/_core/_io_common.py +31 -0
- package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
- package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
- package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
- package/lib/site-packages/trio/_core/_ki.py +271 -0
- package/lib/site-packages/trio/_core/_local.py +104 -0
- package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
- package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
- package/lib/site-packages/trio/_core/_run.py +3148 -0
- package/lib/site-packages/trio/_core/_run_context.py +15 -0
- package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
- package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
- package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
- package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
- package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
- package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
- package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
- package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
- package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
- package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
- package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
- package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
- package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
- package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
- package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
- package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
- package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
- package/lib/site-packages/trio/_core/_traps.py +318 -0
- package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
- package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
- package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
- package/lib/site-packages/trio/_deprecate.py +171 -0
- package/lib/site-packages/trio/_dtls.py +1380 -0
- package/lib/site-packages/trio/_file_io.py +513 -0
- package/lib/site-packages/trio/_highlevel_generic.py +125 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
- package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
- package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
- package/lib/site-packages/trio/_highlevel_socket.py +423 -0
- package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
- package/lib/site-packages/trio/_path.py +289 -0
- package/lib/site-packages/trio/_repl.py +159 -0
- package/lib/site-packages/trio/_signals.py +185 -0
- package/lib/site-packages/trio/_socket.py +1326 -0
- package/lib/site-packages/trio/_ssl.py +964 -0
- package/lib/site-packages/trio/_subprocess.py +1178 -0
- package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
- package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
- package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
- package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
- package/lib/site-packages/trio/_sync.py +908 -0
- package/lib/site-packages/trio/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
- package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
- package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
- package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
- package/lib/site-packages/trio/_tests/test_abc.py +72 -0
- package/lib/site-packages/trio/_tests/test_channel.py +750 -0
- package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
- package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
- package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
- package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
- package/lib/site-packages/trio/_tests/test_exports.py +626 -0
- package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
- package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
- package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
- package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
- package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
- package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
- package/lib/site-packages/trio/_tests/test_path.py +279 -0
- package/lib/site-packages/trio/_tests/test_repl.py +428 -0
- package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
- package/lib/site-packages/trio/_tests/test_signals.py +186 -0
- package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
- package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
- package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
- package/lib/site-packages/trio/_tests/test_sync.py +735 -0
- package/lib/site-packages/trio/_tests/test_testing.py +682 -0
- package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
- package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
- package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
- package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
- package/lib/site-packages/trio/_tests/test_trio.py +8 -0
- package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
- package/lib/site-packages/trio/_tests/test_util.py +349 -0
- package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
- package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
- package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
- package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
- package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
- package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
- package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
- package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
- package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
- package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
- package/lib/site-packages/trio/_threads.py +610 -0
- package/lib/site-packages/trio/_timeouts.py +197 -0
- package/lib/site-packages/trio/_tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
- package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
- package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
- package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
- package/lib/site-packages/trio/_unix_pipes.py +197 -0
- package/lib/site-packages/trio/_util.py +385 -0
- package/lib/site-packages/trio/_version.py +3 -0
- package/lib/site-packages/trio/_wait_for_object.py +67 -0
- package/lib/site-packages/trio/_windows_pipes.py +144 -0
- package/lib/site-packages/trio/abc.py +23 -0
- package/lib/site-packages/trio/from_thread.py +13 -0
- package/lib/site-packages/trio/lowlevel.py +95 -0
- package/lib/site-packages/trio/py.typed +0 -0
- package/lib/site-packages/trio/socket.py +602 -0
- package/lib/site-packages/trio/testing/__init__.py +58 -0
- package/lib/site-packages/trio/testing/_check_streams.py +570 -0
- package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
- package/lib/site-packages/trio/testing/_fake_net.py +584 -0
- package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
- package/lib/site-packages/trio/testing/_network.py +36 -0
- package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
- package/lib/site-packages/trio/testing/_sequencer.py +87 -0
- package/lib/site-packages/trio/testing/_trio_test.py +50 -0
- package/lib/site-packages/trio/to_thread.py +4 -0
- package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
- package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
- package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
- package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
- package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
- package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import errno
|
|
2
|
+
import re
|
|
3
|
+
import socket
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
import trio
|
|
9
|
+
from trio.testing._fake_net import FakeNet
|
|
10
|
+
|
|
11
|
+
# ENOTCONN gives different messages on different platforms
|
|
12
|
+
if sys.platform == "linux":
|
|
13
|
+
ENOTCONN_MSG = r"^\[Errno 107\] (Transport endpoint is|Socket) not connected$"
|
|
14
|
+
elif sys.platform == "darwin":
|
|
15
|
+
ENOTCONN_MSG = r"^\[Errno 57\] Socket is not connected$"
|
|
16
|
+
else:
|
|
17
|
+
ENOTCONN_MSG = r"^\[Errno 10057\] Unknown error$"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def fn() -> FakeNet:
|
|
21
|
+
fn = FakeNet()
|
|
22
|
+
fn.enable()
|
|
23
|
+
return fn
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def test_basic_udp() -> None:
|
|
27
|
+
fn()
|
|
28
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
29
|
+
s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
30
|
+
|
|
31
|
+
await s1.bind(("127.0.0.1", 0))
|
|
32
|
+
ip, port = s1.getsockname()
|
|
33
|
+
assert ip == "127.0.0.1"
|
|
34
|
+
assert port != 0
|
|
35
|
+
|
|
36
|
+
with pytest.raises(
|
|
37
|
+
OSError,
|
|
38
|
+
match=r"^\[\w+ \d+\] Invalid argument$",
|
|
39
|
+
) as exc: # Cannot rebind.
|
|
40
|
+
await s1.bind(("192.0.2.1", 0))
|
|
41
|
+
assert exc.value.errno == errno.EINVAL
|
|
42
|
+
|
|
43
|
+
# Cannot bind multiple sockets to the same address
|
|
44
|
+
with pytest.raises(
|
|
45
|
+
OSError,
|
|
46
|
+
match=r"^\[\w+ \d+\] (Address (already )?in use|Unknown error)$",
|
|
47
|
+
) as exc:
|
|
48
|
+
await s2.bind(("127.0.0.1", port))
|
|
49
|
+
assert exc.value.errno == errno.EADDRINUSE
|
|
50
|
+
|
|
51
|
+
await s2.sendto(b"xyz", s1.getsockname())
|
|
52
|
+
data, addr = await s1.recvfrom(10)
|
|
53
|
+
assert data == b"xyz"
|
|
54
|
+
assert addr == s2.getsockname()
|
|
55
|
+
await s1.sendto(b"abc", s2.getsockname())
|
|
56
|
+
data, addr = await s2.recvfrom(10)
|
|
57
|
+
assert data == b"abc"
|
|
58
|
+
assert addr == s1.getsockname()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def test_msg_trunc() -> None:
|
|
62
|
+
fn()
|
|
63
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
64
|
+
s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
65
|
+
await s1.bind(("127.0.0.1", 0))
|
|
66
|
+
await s2.sendto(b"xyz", s1.getsockname())
|
|
67
|
+
await s1.recvfrom(10)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def test_recv_methods() -> None:
|
|
71
|
+
"""Test all recv methods for codecov"""
|
|
72
|
+
fn()
|
|
73
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
74
|
+
s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
75
|
+
|
|
76
|
+
# receiving on an unbound socket is a bad idea (I think?)
|
|
77
|
+
with pytest.raises(NotImplementedError, match="code will most likely hang"):
|
|
78
|
+
await s2.recv(10)
|
|
79
|
+
|
|
80
|
+
await s1.bind(("127.0.0.1", 0))
|
|
81
|
+
ip, port = s1.getsockname()
|
|
82
|
+
assert ip == "127.0.0.1"
|
|
83
|
+
assert port != 0
|
|
84
|
+
|
|
85
|
+
# recvfrom
|
|
86
|
+
await s2.sendto(b"abc", s1.getsockname())
|
|
87
|
+
data, addr = await s1.recvfrom(10)
|
|
88
|
+
assert data == b"abc"
|
|
89
|
+
assert addr == s2.getsockname()
|
|
90
|
+
|
|
91
|
+
# recv
|
|
92
|
+
await s1.sendto(b"def", s2.getsockname())
|
|
93
|
+
data = await s2.recv(10)
|
|
94
|
+
assert data == b"def"
|
|
95
|
+
|
|
96
|
+
# recvfrom_into
|
|
97
|
+
assert await s1.sendto(b"ghi", s2.getsockname()) == 3
|
|
98
|
+
buf = bytearray(10)
|
|
99
|
+
|
|
100
|
+
with pytest.raises(NotImplementedError, match=r"^partial recvfrom_into$"):
|
|
101
|
+
nbytes, addr = await s2.recvfrom_into(buf, nbytes=2)
|
|
102
|
+
|
|
103
|
+
nbytes, addr = await s2.recvfrom_into(buf)
|
|
104
|
+
assert nbytes == 3
|
|
105
|
+
assert buf == b"ghi" + b"\x00" * 7
|
|
106
|
+
assert addr == s1.getsockname()
|
|
107
|
+
|
|
108
|
+
# recv_into
|
|
109
|
+
assert await s1.sendto(b"jkl", s2.getsockname()) == 3
|
|
110
|
+
buf2 = bytearray(10)
|
|
111
|
+
nbytes = await s2.recv_into(buf2)
|
|
112
|
+
assert nbytes == 3
|
|
113
|
+
assert buf2 == b"jkl" + b"\x00" * 7
|
|
114
|
+
|
|
115
|
+
if sys.platform == "linux" and sys.implementation.name == "cpython":
|
|
116
|
+
flags: int = socket.MSG_MORE
|
|
117
|
+
else:
|
|
118
|
+
flags = 1
|
|
119
|
+
|
|
120
|
+
# Send seems explicitly non-functional
|
|
121
|
+
with pytest.raises(OSError, match=ENOTCONN_MSG) as exc:
|
|
122
|
+
await s2.send(b"mno")
|
|
123
|
+
assert exc.value.errno == errno.ENOTCONN
|
|
124
|
+
with pytest.raises(
|
|
125
|
+
NotImplementedError, match=r"^FakeNet send flags must be 0, not"
|
|
126
|
+
):
|
|
127
|
+
await s2.send(b"mno", flags)
|
|
128
|
+
|
|
129
|
+
# sendto errors
|
|
130
|
+
# it's successfully used earlier
|
|
131
|
+
with pytest.raises(
|
|
132
|
+
NotImplementedError, match=r"^FakeNet send flags must be 0, not"
|
|
133
|
+
):
|
|
134
|
+
await s2.sendto(b"mno", flags, s1.getsockname())
|
|
135
|
+
with pytest.raises(TypeError, match=r"wrong number of arguments$"):
|
|
136
|
+
await s2.sendto(b"mno", flags, s1.getsockname(), "extra arg") # type: ignore[call-overload]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.mark.skipif(
|
|
140
|
+
sys.platform == "win32",
|
|
141
|
+
reason="functions not in socket on windows",
|
|
142
|
+
)
|
|
143
|
+
async def test_nonwindows_functionality() -> None:
|
|
144
|
+
# mypy doesn't support a good way of aborting typechecking on different platforms
|
|
145
|
+
if sys.platform != "win32": # pragma: no branch
|
|
146
|
+
fn()
|
|
147
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
148
|
+
s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
149
|
+
await s2.bind(("127.0.0.1", 0))
|
|
150
|
+
|
|
151
|
+
# sendmsg
|
|
152
|
+
with pytest.raises(OSError, match=ENOTCONN_MSG) as exc:
|
|
153
|
+
await s2.sendmsg([b"mno"])
|
|
154
|
+
assert exc.value.errno == errno.ENOTCONN
|
|
155
|
+
|
|
156
|
+
assert await s1.sendmsg([b"jkl"], (), 0, s2.getsockname()) == 3
|
|
157
|
+
data, ancdata, msg_flags, addr = await s2.recvmsg(10)
|
|
158
|
+
assert data == b"jkl"
|
|
159
|
+
assert ancdata == []
|
|
160
|
+
assert msg_flags == 0
|
|
161
|
+
assert addr == s1.getsockname()
|
|
162
|
+
|
|
163
|
+
# TODO: recvmsg
|
|
164
|
+
|
|
165
|
+
# recvmsg_into
|
|
166
|
+
assert await s1.sendto(b"xyzw", s2.getsockname()) == 4
|
|
167
|
+
buf1 = bytearray(2)
|
|
168
|
+
buf2 = bytearray(3)
|
|
169
|
+
ret = await s2.recvmsg_into([buf1, buf2])
|
|
170
|
+
nbytes, ancdata, msg_flags, addr = ret
|
|
171
|
+
assert nbytes == 4
|
|
172
|
+
assert buf1 == b"xy"
|
|
173
|
+
assert buf2 == b"zw" + b"\x00"
|
|
174
|
+
assert ancdata == []
|
|
175
|
+
assert msg_flags == 0
|
|
176
|
+
assert addr == s1.getsockname()
|
|
177
|
+
|
|
178
|
+
# recvmsg_into with MSG_TRUNC set
|
|
179
|
+
assert await s1.sendto(b"xyzwv", s2.getsockname()) == 5
|
|
180
|
+
buf1 = bytearray(2)
|
|
181
|
+
ret = await s2.recvmsg_into([buf1])
|
|
182
|
+
nbytes, ancdata, msg_flags, addr = ret
|
|
183
|
+
assert nbytes == 2
|
|
184
|
+
assert buf1 == b"xy"
|
|
185
|
+
assert ancdata == []
|
|
186
|
+
assert msg_flags == socket.MSG_TRUNC
|
|
187
|
+
assert addr == s1.getsockname()
|
|
188
|
+
|
|
189
|
+
with pytest.raises(
|
|
190
|
+
AttributeError,
|
|
191
|
+
match=r"^'FakeSocket' object has no attribute 'share'$",
|
|
192
|
+
):
|
|
193
|
+
await s1.share(0) # type: ignore[attr-defined]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@pytest.mark.skipif(
|
|
197
|
+
sys.platform != "win32",
|
|
198
|
+
reason="windows-specific fakesocket testing",
|
|
199
|
+
)
|
|
200
|
+
async def test_windows_functionality() -> None:
|
|
201
|
+
# mypy doesn't support a good way of aborting typechecking on different platforms
|
|
202
|
+
if sys.platform == "win32": # pragma: no branch
|
|
203
|
+
fn()
|
|
204
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
205
|
+
s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
206
|
+
await s1.bind(("127.0.0.1", 0))
|
|
207
|
+
with pytest.raises(
|
|
208
|
+
AttributeError,
|
|
209
|
+
match=r"^'FakeSocket' object has no attribute 'sendmsg'$",
|
|
210
|
+
):
|
|
211
|
+
await s1.sendmsg([b"jkl"], (), 0, s2.getsockname()) # type: ignore[attr-defined]
|
|
212
|
+
with pytest.raises(
|
|
213
|
+
AttributeError,
|
|
214
|
+
match=r"^'FakeSocket' object has no attribute 'recvmsg'$",
|
|
215
|
+
):
|
|
216
|
+
s2.recvmsg(0) # type: ignore[attr-defined]
|
|
217
|
+
with pytest.raises(
|
|
218
|
+
AttributeError,
|
|
219
|
+
match=r"^'FakeSocket' object has no attribute 'recvmsg_into'$",
|
|
220
|
+
):
|
|
221
|
+
s2.recvmsg_into([]) # type: ignore[attr-defined]
|
|
222
|
+
with pytest.raises(NotImplementedError):
|
|
223
|
+
s1.share(0)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
async def test_basic_tcp() -> None:
|
|
227
|
+
fn()
|
|
228
|
+
with pytest.raises(NotImplementedError):
|
|
229
|
+
trio.socket.socket()
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
async def test_not_implemented_functions() -> None:
|
|
233
|
+
fn()
|
|
234
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
235
|
+
|
|
236
|
+
# getsockopt
|
|
237
|
+
with pytest.raises(
|
|
238
|
+
OSError,
|
|
239
|
+
match=r"^FakeNet doesn't implement getsockopt\(\d, \d\)$",
|
|
240
|
+
):
|
|
241
|
+
s1.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)
|
|
242
|
+
|
|
243
|
+
# setsockopt
|
|
244
|
+
with pytest.raises(
|
|
245
|
+
NotImplementedError,
|
|
246
|
+
match=r"^FakeNet always has IPV6_V6ONLY=True$",
|
|
247
|
+
):
|
|
248
|
+
s1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
|
249
|
+
with pytest.raises(
|
|
250
|
+
OSError,
|
|
251
|
+
match=r"^FakeNet doesn't implement setsockopt\(\d+, \d+, \.\.\.\)$",
|
|
252
|
+
):
|
|
253
|
+
s1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, True)
|
|
254
|
+
with pytest.raises(
|
|
255
|
+
OSError,
|
|
256
|
+
match=r"^FakeNet doesn't implement setsockopt\(\d+, \d+, \.\.\.\)$",
|
|
257
|
+
):
|
|
258
|
+
s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
259
|
+
|
|
260
|
+
# set_inheritable
|
|
261
|
+
s1.set_inheritable(False)
|
|
262
|
+
with pytest.raises(
|
|
263
|
+
NotImplementedError,
|
|
264
|
+
match=r"^FakeNet can't make inheritable sockets$",
|
|
265
|
+
):
|
|
266
|
+
s1.set_inheritable(True)
|
|
267
|
+
|
|
268
|
+
# get_inheritable
|
|
269
|
+
assert not s1.get_inheritable()
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def test_getpeername() -> None:
|
|
273
|
+
fn()
|
|
274
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
275
|
+
with pytest.raises(OSError, match=ENOTCONN_MSG) as exc:
|
|
276
|
+
s1.getpeername()
|
|
277
|
+
assert exc.value.errno == errno.ENOTCONN
|
|
278
|
+
|
|
279
|
+
await s1.bind(("127.0.0.1", 0))
|
|
280
|
+
|
|
281
|
+
with pytest.raises(
|
|
282
|
+
AssertionError,
|
|
283
|
+
match=r"^This method seems to assume that self._binding has a remote UDPEndpoint$",
|
|
284
|
+
):
|
|
285
|
+
s1.getpeername()
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
async def test_init() -> None:
|
|
289
|
+
fn()
|
|
290
|
+
with pytest.raises(
|
|
291
|
+
NotImplementedError,
|
|
292
|
+
match=re.escape(
|
|
293
|
+
f"FakeNet doesn't (yet) support type={trio.socket.SOCK_STREAM}",
|
|
294
|
+
),
|
|
295
|
+
):
|
|
296
|
+
s1 = trio.socket.socket()
|
|
297
|
+
|
|
298
|
+
# getsockname on unbound ipv4 socket
|
|
299
|
+
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
|
|
300
|
+
assert s1.getsockname() == ("0.0.0.0", 0)
|
|
301
|
+
|
|
302
|
+
# getsockname on bound ipv4 socket
|
|
303
|
+
await s1.bind(("0.0.0.0", 0))
|
|
304
|
+
ip, port = s1.getsockname()
|
|
305
|
+
assert ip == "127.0.0.1"
|
|
306
|
+
assert port != 0
|
|
307
|
+
|
|
308
|
+
# getsockname on unbound ipv6 socket
|
|
309
|
+
s2 = trio.socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM)
|
|
310
|
+
assert s2.getsockname() == ("::", 0)
|
|
311
|
+
|
|
312
|
+
# getsockname on bound ipv6 socket
|
|
313
|
+
await s2.bind(("::", 0))
|
|
314
|
+
ip, port, *_ = s2.getsockname()
|
|
315
|
+
assert ip == "::1"
|
|
316
|
+
assert port != 0
|
|
317
|
+
assert _ == [0, 0]
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import io
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
from unittest import mock
|
|
9
|
+
from unittest.mock import sentinel
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
import trio
|
|
14
|
+
from trio import _core, _file_io
|
|
15
|
+
from trio._file_io import _FILE_ASYNC_METHODS, _FILE_SYNC_ATTRS, AsyncIOWrapper
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
import pathlib
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def path(tmp_path: pathlib.Path) -> str:
|
|
23
|
+
return os.fspath(tmp_path / "test")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def wrapped() -> mock.Mock:
|
|
28
|
+
return mock.Mock(spec_set=io.StringIO)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def async_file(wrapped: mock.Mock) -> AsyncIOWrapper[mock.Mock]:
|
|
33
|
+
return trio.wrap_file(wrapped)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_wrap_invalid() -> None:
|
|
37
|
+
with pytest.raises(TypeError):
|
|
38
|
+
trio.wrap_file("")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_wrap_non_iobase() -> None:
|
|
42
|
+
class FakeFile:
|
|
43
|
+
def close(self) -> None: # pragma: no cover
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def write(self) -> None: # pragma: no cover
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
wrapped = FakeFile()
|
|
50
|
+
assert not isinstance(wrapped, io.IOBase)
|
|
51
|
+
|
|
52
|
+
async_file = trio.wrap_file(wrapped)
|
|
53
|
+
assert isinstance(async_file, AsyncIOWrapper)
|
|
54
|
+
|
|
55
|
+
del FakeFile.write
|
|
56
|
+
|
|
57
|
+
with pytest.raises(TypeError):
|
|
58
|
+
trio.wrap_file(FakeFile())
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_wrapped_property(
|
|
62
|
+
async_file: AsyncIOWrapper[mock.Mock],
|
|
63
|
+
wrapped: mock.Mock,
|
|
64
|
+
) -> None:
|
|
65
|
+
assert async_file.wrapped is wrapped
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_dir_matches_wrapped(
|
|
69
|
+
async_file: AsyncIOWrapper[mock.Mock],
|
|
70
|
+
wrapped: mock.Mock,
|
|
71
|
+
) -> None:
|
|
72
|
+
attrs = _FILE_SYNC_ATTRS.union(_FILE_ASYNC_METHODS)
|
|
73
|
+
|
|
74
|
+
# all supported attrs in wrapped should be available in async_file
|
|
75
|
+
assert all(attr in dir(async_file) for attr in attrs if attr in dir(wrapped))
|
|
76
|
+
# all supported attrs not in wrapped should not be available in async_file
|
|
77
|
+
assert not any(
|
|
78
|
+
attr in dir(async_file) for attr in attrs if attr not in dir(wrapped)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_unsupported_not_forwarded() -> None:
|
|
83
|
+
class FakeFile(io.RawIOBase):
|
|
84
|
+
def unsupported_attr(self) -> None: # pragma: no cover
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
async_file = trio.wrap_file(FakeFile())
|
|
88
|
+
|
|
89
|
+
assert hasattr(async_file.wrapped, "unsupported_attr")
|
|
90
|
+
|
|
91
|
+
with pytest.raises(AttributeError):
|
|
92
|
+
# B018 "useless expression"
|
|
93
|
+
async_file.unsupported_attr # type: ignore[attr-defined] # noqa: B018
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_type_stubs_match_lists() -> None:
|
|
97
|
+
"""Check the manual stubs match the list of wrapped methods."""
|
|
98
|
+
# Fetch the module's source code.
|
|
99
|
+
assert _file_io.__spec__ is not None
|
|
100
|
+
loader = _file_io.__spec__.loader
|
|
101
|
+
assert isinstance(loader, importlib.abc.SourceLoader)
|
|
102
|
+
source = io.StringIO(loader.get_source("trio._file_io"))
|
|
103
|
+
|
|
104
|
+
# Find the class, then find the TYPE_CHECKING block.
|
|
105
|
+
for line in source:
|
|
106
|
+
if "class AsyncIOWrapper" in line:
|
|
107
|
+
break
|
|
108
|
+
else: # pragma: no cover - should always find this
|
|
109
|
+
pytest.fail("No class definition line?")
|
|
110
|
+
|
|
111
|
+
for line in source:
|
|
112
|
+
if "if TYPE_CHECKING" in line:
|
|
113
|
+
break
|
|
114
|
+
else: # pragma: no cover - should always find this
|
|
115
|
+
pytest.fail("No TYPE CHECKING line?")
|
|
116
|
+
|
|
117
|
+
# Now we should be at the type checking block.
|
|
118
|
+
found: list[tuple[str, str]] = []
|
|
119
|
+
for line in source: # pragma: no branch - expected to break early
|
|
120
|
+
if line.strip() and not line.startswith(" " * 8):
|
|
121
|
+
break # Dedented out of the if TYPE_CHECKING block.
|
|
122
|
+
match = re.match(r"\s*(async )?def ([a-zA-Z0-9_]+)\(", line)
|
|
123
|
+
if match is not None:
|
|
124
|
+
kind = "async" if match.group(1) is not None else "sync"
|
|
125
|
+
found.append((match.group(2), kind))
|
|
126
|
+
|
|
127
|
+
# Compare two lists so that we can easily see duplicates, and see what is different overall.
|
|
128
|
+
expected = [(fname, "async") for fname in _FILE_ASYNC_METHODS]
|
|
129
|
+
expected += [(fname, "sync") for fname in _FILE_SYNC_ATTRS]
|
|
130
|
+
# Ignore order, error if duplicates are present.
|
|
131
|
+
found.sort()
|
|
132
|
+
expected.sort()
|
|
133
|
+
assert found == expected
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_sync_attrs_forwarded(
|
|
137
|
+
async_file: AsyncIOWrapper[mock.Mock],
|
|
138
|
+
wrapped: mock.Mock,
|
|
139
|
+
) -> None:
|
|
140
|
+
for attr_name in _FILE_SYNC_ATTRS:
|
|
141
|
+
if attr_name not in dir(async_file):
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
assert getattr(async_file, attr_name) is getattr(wrapped, attr_name)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_sync_attrs_match_wrapper(
|
|
148
|
+
async_file: AsyncIOWrapper[mock.Mock],
|
|
149
|
+
wrapped: mock.Mock,
|
|
150
|
+
) -> None:
|
|
151
|
+
for attr_name in _FILE_SYNC_ATTRS:
|
|
152
|
+
if attr_name in dir(async_file):
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
with pytest.raises(AttributeError):
|
|
156
|
+
getattr(async_file, attr_name)
|
|
157
|
+
|
|
158
|
+
with pytest.raises(AttributeError):
|
|
159
|
+
getattr(wrapped, attr_name)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_async_methods_generated_once(async_file: AsyncIOWrapper[mock.Mock]) -> None:
|
|
163
|
+
for meth_name in _FILE_ASYNC_METHODS:
|
|
164
|
+
if meth_name not in dir(async_file):
|
|
165
|
+
continue
|
|
166
|
+
|
|
167
|
+
assert getattr(async_file, meth_name) is getattr(async_file, meth_name)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# I gave up on typing this one
|
|
171
|
+
def test_async_methods_signature(async_file: AsyncIOWrapper[mock.Mock]) -> None:
|
|
172
|
+
# use read as a representative of all async methods
|
|
173
|
+
assert async_file.read.__name__ == "read"
|
|
174
|
+
assert async_file.read.__qualname__ == "AsyncIOWrapper.read"
|
|
175
|
+
|
|
176
|
+
assert async_file.read.__doc__ is not None
|
|
177
|
+
assert "io.StringIO.read" in async_file.read.__doc__
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
async def test_async_methods_wrap(
|
|
181
|
+
async_file: AsyncIOWrapper[mock.Mock],
|
|
182
|
+
wrapped: mock.Mock,
|
|
183
|
+
) -> None:
|
|
184
|
+
for meth_name in _FILE_ASYNC_METHODS:
|
|
185
|
+
if meth_name not in dir(async_file):
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
meth = getattr(async_file, meth_name)
|
|
189
|
+
wrapped_meth = getattr(wrapped, meth_name)
|
|
190
|
+
|
|
191
|
+
value = await meth(sentinel.argument, keyword=sentinel.keyword)
|
|
192
|
+
|
|
193
|
+
wrapped_meth.assert_called_once_with(
|
|
194
|
+
sentinel.argument,
|
|
195
|
+
keyword=sentinel.keyword,
|
|
196
|
+
)
|
|
197
|
+
assert value == wrapped_meth()
|
|
198
|
+
|
|
199
|
+
wrapped.reset_mock()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_async_methods_match_wrapper(
|
|
203
|
+
async_file: AsyncIOWrapper[mock.Mock],
|
|
204
|
+
wrapped: mock.Mock,
|
|
205
|
+
) -> None:
|
|
206
|
+
for meth_name in _FILE_ASYNC_METHODS:
|
|
207
|
+
if meth_name in dir(async_file):
|
|
208
|
+
continue
|
|
209
|
+
|
|
210
|
+
with pytest.raises(AttributeError):
|
|
211
|
+
getattr(async_file, meth_name)
|
|
212
|
+
|
|
213
|
+
with pytest.raises(AttributeError):
|
|
214
|
+
getattr(wrapped, meth_name)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
async def test_open(path: pathlib.Path) -> None:
|
|
218
|
+
f = await trio.open_file(path, "w")
|
|
219
|
+
|
|
220
|
+
assert isinstance(f, AsyncIOWrapper)
|
|
221
|
+
|
|
222
|
+
await f.aclose()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
async def test_open_context_manager(path: pathlib.Path) -> None:
|
|
226
|
+
async with await trio.open_file(path, "w") as f:
|
|
227
|
+
assert isinstance(f, AsyncIOWrapper)
|
|
228
|
+
assert not f.closed
|
|
229
|
+
|
|
230
|
+
assert f.closed
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
async def test_async_iter() -> None:
|
|
234
|
+
async_file = trio.wrap_file(io.StringIO("test\nfoo\nbar"))
|
|
235
|
+
expected = list(async_file.wrapped)
|
|
236
|
+
async_file.wrapped.seek(0)
|
|
237
|
+
|
|
238
|
+
result = [line async for line in async_file]
|
|
239
|
+
|
|
240
|
+
assert result == expected
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
async def test_aclose_cancelled(path: pathlib.Path) -> None:
|
|
244
|
+
with _core.CancelScope() as cscope:
|
|
245
|
+
f = await trio.open_file(path, "w")
|
|
246
|
+
cscope.cancel()
|
|
247
|
+
|
|
248
|
+
with pytest.raises(_core.Cancelled):
|
|
249
|
+
await f.write("a")
|
|
250
|
+
|
|
251
|
+
with pytest.raises(_core.Cancelled):
|
|
252
|
+
await f.aclose()
|
|
253
|
+
|
|
254
|
+
assert f.closed
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
async def test_detach_rewraps_asynciobase(tmp_path: pathlib.Path) -> None:
|
|
258
|
+
tmp_file = tmp_path / "filename"
|
|
259
|
+
tmp_file.touch()
|
|
260
|
+
# flake8-async does not like opening files in async mode
|
|
261
|
+
with open(tmp_file, mode="rb", buffering=0) as raw: # noqa: ASYNC230
|
|
262
|
+
buffered = io.BufferedReader(raw)
|
|
263
|
+
|
|
264
|
+
async_file = trio.wrap_file(buffered)
|
|
265
|
+
|
|
266
|
+
detached = await async_file.detach()
|
|
267
|
+
|
|
268
|
+
assert isinstance(detached, AsyncIOWrapper)
|
|
269
|
+
assert detached.wrapped is raw
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import NoReturn
|
|
4
|
+
|
|
5
|
+
import attrs
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from .._highlevel_generic import StapledStream
|
|
9
|
+
from ..abc import ReceiveStream, SendStream
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@attrs.define(slots=False)
|
|
13
|
+
class RecordSendStream(SendStream):
|
|
14
|
+
record: list[str | tuple[str, object]] = attrs.Factory(list)
|
|
15
|
+
|
|
16
|
+
async def send_all(self, data: object) -> None:
|
|
17
|
+
self.record.append(("send_all", data))
|
|
18
|
+
|
|
19
|
+
async def wait_send_all_might_not_block(self) -> None:
|
|
20
|
+
self.record.append("wait_send_all_might_not_block")
|
|
21
|
+
|
|
22
|
+
async def aclose(self) -> None:
|
|
23
|
+
self.record.append("aclose")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@attrs.define(slots=False)
|
|
27
|
+
class RecordReceiveStream(ReceiveStream):
|
|
28
|
+
record: list[str | tuple[str, int | None]] = attrs.Factory(list)
|
|
29
|
+
|
|
30
|
+
async def receive_some(self, max_bytes: int | None = None) -> bytes:
|
|
31
|
+
self.record.append(("receive_some", max_bytes))
|
|
32
|
+
return b""
|
|
33
|
+
|
|
34
|
+
async def aclose(self) -> None:
|
|
35
|
+
self.record.append("aclose")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def test_StapledStream() -> None:
|
|
39
|
+
send_stream = RecordSendStream()
|
|
40
|
+
receive_stream = RecordReceiveStream()
|
|
41
|
+
stapled = StapledStream(send_stream, receive_stream)
|
|
42
|
+
|
|
43
|
+
assert stapled.send_stream is send_stream
|
|
44
|
+
assert stapled.receive_stream is receive_stream
|
|
45
|
+
|
|
46
|
+
await stapled.send_all(b"foo")
|
|
47
|
+
await stapled.wait_send_all_might_not_block()
|
|
48
|
+
assert send_stream.record == [
|
|
49
|
+
("send_all", b"foo"),
|
|
50
|
+
"wait_send_all_might_not_block",
|
|
51
|
+
]
|
|
52
|
+
send_stream.record.clear()
|
|
53
|
+
|
|
54
|
+
await stapled.send_eof()
|
|
55
|
+
assert send_stream.record == ["aclose"]
|
|
56
|
+
send_stream.record.clear()
|
|
57
|
+
|
|
58
|
+
async def fake_send_eof() -> None:
|
|
59
|
+
send_stream.record.append("send_eof")
|
|
60
|
+
|
|
61
|
+
send_stream.send_eof = fake_send_eof # type: ignore[attr-defined]
|
|
62
|
+
await stapled.send_eof()
|
|
63
|
+
assert send_stream.record == ["send_eof"]
|
|
64
|
+
|
|
65
|
+
send_stream.record.clear()
|
|
66
|
+
assert receive_stream.record == []
|
|
67
|
+
|
|
68
|
+
await stapled.receive_some(1234)
|
|
69
|
+
assert receive_stream.record == [("receive_some", 1234)]
|
|
70
|
+
assert send_stream.record == []
|
|
71
|
+
receive_stream.record.clear()
|
|
72
|
+
|
|
73
|
+
await stapled.aclose()
|
|
74
|
+
assert receive_stream.record == ["aclose"]
|
|
75
|
+
assert send_stream.record == ["aclose"]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def test_StapledStream_with_erroring_close() -> None:
|
|
79
|
+
# Make sure that if one of the aclose methods errors out, then the other
|
|
80
|
+
# one still gets called.
|
|
81
|
+
class BrokenSendStream(RecordSendStream):
|
|
82
|
+
async def aclose(self) -> NoReturn:
|
|
83
|
+
await super().aclose()
|
|
84
|
+
raise ValueError("send error")
|
|
85
|
+
|
|
86
|
+
class BrokenReceiveStream(RecordReceiveStream):
|
|
87
|
+
async def aclose(self) -> NoReturn:
|
|
88
|
+
await super().aclose()
|
|
89
|
+
raise ValueError("recv error")
|
|
90
|
+
|
|
91
|
+
stapled = StapledStream(BrokenSendStream(), BrokenReceiveStream())
|
|
92
|
+
|
|
93
|
+
with pytest.raises(ValueError, match=r"^(send|recv) error$") as excinfo:
|
|
94
|
+
await stapled.aclose()
|
|
95
|
+
assert isinstance(excinfo.value.__context__, ValueError)
|
|
96
|
+
|
|
97
|
+
assert stapled.send_stream.record == ["aclose"]
|
|
98
|
+
assert stapled.receive_stream.record == ["aclose"]
|