@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,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""
|