@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,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import TYPE_CHECKING, Protocol, TypeVar
|
|
6
|
+
|
|
7
|
+
import trio
|
|
8
|
+
from trio.socket import SOCK_STREAM, socket
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Generator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Closable(Protocol):
|
|
15
|
+
def close(self) -> None: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
CloseT = TypeVar("CloseT", bound=Closable)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from trio.socket import AF_UNIX
|
|
23
|
+
|
|
24
|
+
has_unix = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
has_unix = False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@contextmanager
|
|
30
|
+
def close_on_error(obj: CloseT) -> Generator[CloseT, None, None]:
|
|
31
|
+
try:
|
|
32
|
+
yield obj
|
|
33
|
+
except:
|
|
34
|
+
obj.close()
|
|
35
|
+
raise
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def open_unix_socket(
|
|
39
|
+
filename: str | bytes | os.PathLike[str] | os.PathLike[bytes],
|
|
40
|
+
) -> trio.SocketStream:
|
|
41
|
+
"""Opens a connection to the specified
|
|
42
|
+
`Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`__.
|
|
43
|
+
|
|
44
|
+
You must have read/write permission on the specified file to connect.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
filename (str or bytes): The filename to open the connection to.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
SocketStream: a :class:`~trio.abc.Stream` connected to the given file.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
OSError: If the socket file could not be connected to.
|
|
54
|
+
RuntimeError: If AF_UNIX sockets are not supported.
|
|
55
|
+
"""
|
|
56
|
+
if not has_unix:
|
|
57
|
+
raise RuntimeError("Unix sockets are not supported on this platform")
|
|
58
|
+
|
|
59
|
+
# much more simplified logic vs tcp sockets - one socket type and only one
|
|
60
|
+
# possible location to connect to
|
|
61
|
+
sock = socket(AF_UNIX, SOCK_STREAM)
|
|
62
|
+
with close_on_error(sock):
|
|
63
|
+
await sock.connect(os.fspath(filename))
|
|
64
|
+
|
|
65
|
+
return trio.SocketStream(sock)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import errno
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Awaitable, Callable
|
|
7
|
+
from typing import Any, NoReturn, TypeVar
|
|
8
|
+
|
|
9
|
+
import trio
|
|
10
|
+
|
|
11
|
+
# Errors that accept(2) can return, and which indicate that the system is
|
|
12
|
+
# overloaded
|
|
13
|
+
ACCEPT_CAPACITY_ERRNOS = {
|
|
14
|
+
errno.EMFILE,
|
|
15
|
+
errno.ENFILE,
|
|
16
|
+
errno.ENOMEM,
|
|
17
|
+
errno.ENOBUFS,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# How long to sleep when we get one of those errors
|
|
21
|
+
SLEEP_TIME = 0.100
|
|
22
|
+
|
|
23
|
+
# The logger we use to complain when this happens
|
|
24
|
+
LOGGER = logging.getLogger("trio.serve_listeners")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
StreamT = TypeVar("StreamT", bound=trio.abc.AsyncResource)
|
|
28
|
+
ListenerT = TypeVar("ListenerT", bound=trio.abc.Listener[Any]) # type: ignore[explicit-any]
|
|
29
|
+
Handler = Callable[[StreamT], Awaitable[object]]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def _run_handler(stream: StreamT, handler: Handler[StreamT]) -> None:
|
|
33
|
+
try:
|
|
34
|
+
await handler(stream)
|
|
35
|
+
finally:
|
|
36
|
+
await trio.aclose_forcefully(stream)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def _serve_one_listener(
|
|
40
|
+
listener: trio.abc.Listener[StreamT],
|
|
41
|
+
handler_nursery: trio.Nursery,
|
|
42
|
+
handler: Handler[StreamT],
|
|
43
|
+
) -> NoReturn:
|
|
44
|
+
async with listener:
|
|
45
|
+
while True:
|
|
46
|
+
try:
|
|
47
|
+
stream = await listener.accept()
|
|
48
|
+
except OSError as exc:
|
|
49
|
+
if exc.errno in ACCEPT_CAPACITY_ERRNOS:
|
|
50
|
+
LOGGER.error(
|
|
51
|
+
"accept returned %s (%s); retrying in %s seconds",
|
|
52
|
+
errno.errorcode[exc.errno],
|
|
53
|
+
os.strerror(exc.errno),
|
|
54
|
+
SLEEP_TIME,
|
|
55
|
+
exc_info=True,
|
|
56
|
+
)
|
|
57
|
+
await trio.sleep(SLEEP_TIME)
|
|
58
|
+
else:
|
|
59
|
+
raise
|
|
60
|
+
else:
|
|
61
|
+
handler_nursery.start_soon(_run_handler, stream, handler)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# This cannot be typed correctly, we need generic typevar bounds / HKT to indicate the
|
|
65
|
+
# relationship between StreamT & ListenerT.
|
|
66
|
+
# https://github.com/python/typing/issues/1226
|
|
67
|
+
# https://github.com/python/typing/issues/548
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def serve_listeners( # type: ignore[explicit-any]
|
|
71
|
+
handler: Handler[StreamT],
|
|
72
|
+
listeners: list[ListenerT],
|
|
73
|
+
*,
|
|
74
|
+
handler_nursery: trio.Nursery | None = None,
|
|
75
|
+
task_status: trio.TaskStatus[list[ListenerT]] = trio.TASK_STATUS_IGNORED,
|
|
76
|
+
) -> NoReturn:
|
|
77
|
+
r"""Listen for incoming connections on ``listeners``, and for each one
|
|
78
|
+
start a task running ``handler(stream)``.
|
|
79
|
+
|
|
80
|
+
.. warning::
|
|
81
|
+
|
|
82
|
+
If ``handler`` raises an exception, then this function doesn't do
|
|
83
|
+
anything special to catch it – so by default the exception will
|
|
84
|
+
propagate out and crash your server. If you don't want this, then catch
|
|
85
|
+
exceptions inside your ``handler``, or use a ``handler_nursery`` object
|
|
86
|
+
that responds to exceptions in some other way.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
|
|
90
|
+
handler: An async callable, that will be invoked like
|
|
91
|
+
``handler_nursery.start_soon(handler, stream)`` for each incoming
|
|
92
|
+
connection.
|
|
93
|
+
|
|
94
|
+
listeners: A list of :class:`~trio.abc.Listener` objects.
|
|
95
|
+
:func:`serve_listeners` takes responsibility for closing them.
|
|
96
|
+
|
|
97
|
+
handler_nursery: The nursery used to start handlers, or any object with
|
|
98
|
+
a ``start_soon`` method. If ``None`` (the default), then
|
|
99
|
+
:func:`serve_listeners` will create a new nursery internally and use
|
|
100
|
+
that.
|
|
101
|
+
|
|
102
|
+
task_status: This function can be used with ``nursery.start``, which
|
|
103
|
+
will return ``listeners``.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
|
|
107
|
+
This function never returns unless cancelled.
|
|
108
|
+
|
|
109
|
+
Resource handling:
|
|
110
|
+
|
|
111
|
+
If ``handler`` neglects to close the ``stream``, then it will be closed
|
|
112
|
+
using :func:`trio.aclose_forcefully`.
|
|
113
|
+
|
|
114
|
+
Error handling:
|
|
115
|
+
|
|
116
|
+
Most errors coming from :meth:`~trio.abc.Listener.accept` are allowed to
|
|
117
|
+
propagate out (crashing the server in the process). However, some errors –
|
|
118
|
+
those which indicate that the server is temporarily overloaded – are
|
|
119
|
+
handled specially. These are :class:`OSError`\s with one of the following
|
|
120
|
+
errnos:
|
|
121
|
+
|
|
122
|
+
* ``EMFILE``: process is out of file descriptors
|
|
123
|
+
* ``ENFILE``: system is out of file descriptors
|
|
124
|
+
* ``ENOBUFS``, ``ENOMEM``: the kernel hit some sort of memory limitation
|
|
125
|
+
when trying to create a socket object
|
|
126
|
+
|
|
127
|
+
When :func:`serve_listeners` gets one of these errors, then it:
|
|
128
|
+
|
|
129
|
+
* Logs the error to the standard library logger ``trio.serve_listeners``
|
|
130
|
+
(level = ERROR, with exception information included). By default this
|
|
131
|
+
causes it to be printed to stderr.
|
|
132
|
+
* Waits 100 ms before calling ``accept`` again, in hopes that the
|
|
133
|
+
system will recover.
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
async with trio.open_nursery() as nursery:
|
|
137
|
+
if handler_nursery is None:
|
|
138
|
+
handler_nursery = nursery
|
|
139
|
+
for listener in listeners:
|
|
140
|
+
nursery.start_soon(_serve_one_listener, listener, handler_nursery, handler)
|
|
141
|
+
# The listeners are already queueing connections when we're called,
|
|
142
|
+
# but we wait until the end to call started() just in case we get an
|
|
143
|
+
# error or whatever.
|
|
144
|
+
task_status.started(listeners)
|
|
145
|
+
|
|
146
|
+
raise AssertionError(
|
|
147
|
+
"_serve_one_listener should never complete",
|
|
148
|
+
) # pragma: no cover
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# "High-level" networking interface
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import errno
|
|
5
|
+
from contextlib import contextmanager, suppress
|
|
6
|
+
from typing import TYPE_CHECKING, overload
|
|
7
|
+
|
|
8
|
+
import trio
|
|
9
|
+
|
|
10
|
+
from . import socket as tsocket
|
|
11
|
+
from ._util import ConflictDetector, final
|
|
12
|
+
from .abc import HalfCloseableStream, Listener
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Generator
|
|
16
|
+
|
|
17
|
+
from ._socket import SocketType
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
if sys.version_info >= (3, 12):
|
|
22
|
+
# NOTE: this isn't in the `TYPE_CHECKING` since for some reason
|
|
23
|
+
# sphinx doesn't autoreload this module for SocketStream
|
|
24
|
+
# (hypothesis: it's our module renaming magic)
|
|
25
|
+
from collections.abc import Buffer
|
|
26
|
+
elif TYPE_CHECKING:
|
|
27
|
+
from typing_extensions import Buffer
|
|
28
|
+
|
|
29
|
+
# XX TODO: this number was picked arbitrarily. We should do experiments to
|
|
30
|
+
# tune it. (Or make it dynamic -- one idea is to start small and increase it
|
|
31
|
+
# if we observe single reads filling up the whole buffer, at least within some
|
|
32
|
+
# limits.)
|
|
33
|
+
DEFAULT_RECEIVE_SIZE = 65536
|
|
34
|
+
|
|
35
|
+
_closed_stream_errnos = {
|
|
36
|
+
# Unix
|
|
37
|
+
errno.EBADF,
|
|
38
|
+
# Windows
|
|
39
|
+
errno.ENOTSOCK,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@contextmanager
|
|
44
|
+
def _translate_socket_errors_to_stream_errors() -> Generator[None, None, None]:
|
|
45
|
+
try:
|
|
46
|
+
yield
|
|
47
|
+
except OSError as exc:
|
|
48
|
+
if exc.errno in _closed_stream_errnos:
|
|
49
|
+
raise trio.ClosedResourceError("this socket was already closed") from None
|
|
50
|
+
else:
|
|
51
|
+
raise trio.BrokenResourceError(f"socket connection broken: {exc}") from exc
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@final
|
|
55
|
+
class SocketStream(HalfCloseableStream):
|
|
56
|
+
"""An implementation of the :class:`trio.abc.HalfCloseableStream`
|
|
57
|
+
interface based on a raw network socket.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
socket: The Trio socket object to wrap. Must have type ``SOCK_STREAM``,
|
|
61
|
+
and be connected.
|
|
62
|
+
|
|
63
|
+
By default for TCP sockets, :class:`SocketStream` enables ``TCP_NODELAY``,
|
|
64
|
+
and (on platforms where it's supported) enables ``TCP_NOTSENT_LOWAT`` with
|
|
65
|
+
a reasonable buffer size (currently 16 KiB) – see `issue #72
|
|
66
|
+
<https://github.com/python-trio/trio/issues/72>`__ for discussion. You can
|
|
67
|
+
of course override these defaults by calling :meth:`setsockopt`.
|
|
68
|
+
|
|
69
|
+
Once a :class:`SocketStream` object is constructed, it implements the full
|
|
70
|
+
:class:`trio.abc.HalfCloseableStream` interface. In addition, it provides
|
|
71
|
+
a few extra features:
|
|
72
|
+
|
|
73
|
+
.. attribute:: socket
|
|
74
|
+
|
|
75
|
+
The Trio socket object that this stream wraps.
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, socket: SocketType) -> None:
|
|
80
|
+
if not isinstance(socket, tsocket.SocketType):
|
|
81
|
+
raise TypeError("SocketStream requires a Trio socket object")
|
|
82
|
+
if socket.type != tsocket.SOCK_STREAM:
|
|
83
|
+
raise ValueError("SocketStream requires a SOCK_STREAM socket")
|
|
84
|
+
|
|
85
|
+
self.socket = socket
|
|
86
|
+
self._send_conflict_detector = ConflictDetector(
|
|
87
|
+
"another task is currently sending data on this SocketStream",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Socket defaults:
|
|
91
|
+
|
|
92
|
+
# Not supported on e.g. unix domain sockets
|
|
93
|
+
with suppress(OSError):
|
|
94
|
+
self.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, True)
|
|
95
|
+
|
|
96
|
+
if hasattr(tsocket, "TCP_NOTSENT_LOWAT"):
|
|
97
|
+
# 16 KiB is pretty arbitrary and could probably do with some
|
|
98
|
+
# tuning. (Apple is also setting this by default in CFNetwork
|
|
99
|
+
# apparently -- I'm curious what value they're using, though I
|
|
100
|
+
# couldn't find it online trivially. CFNetwork-129.20 source
|
|
101
|
+
# has no mentions of TCP_NOTSENT_LOWAT. This presentation says
|
|
102
|
+
# "typically 8 kilobytes":
|
|
103
|
+
# http://devstreaming.apple.com/videos/wwdc/2015/719ui2k57m/719/719_your_app_and_next_generation_networks.pdf?dl=1
|
|
104
|
+
# ). The theory is that you want it to be bandwidth *
|
|
105
|
+
# rescheduling interval.
|
|
106
|
+
with suppress(OSError):
|
|
107
|
+
self.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NOTSENT_LOWAT, 2**14)
|
|
108
|
+
|
|
109
|
+
async def send_all(self, data: bytes | bytearray | memoryview) -> None:
|
|
110
|
+
if self.socket.did_shutdown_SHUT_WR:
|
|
111
|
+
raise trio.ClosedResourceError("can't send data after sending EOF")
|
|
112
|
+
with self._send_conflict_detector:
|
|
113
|
+
with _translate_socket_errors_to_stream_errors():
|
|
114
|
+
with memoryview(data) as data:
|
|
115
|
+
if not data:
|
|
116
|
+
if self.socket.fileno() == -1:
|
|
117
|
+
raise trio.ClosedResourceError("socket was already closed")
|
|
118
|
+
await trio.lowlevel.checkpoint()
|
|
119
|
+
return
|
|
120
|
+
total_sent = 0
|
|
121
|
+
while total_sent < len(data):
|
|
122
|
+
with data[total_sent:] as remaining:
|
|
123
|
+
sent = await self.socket.send(remaining)
|
|
124
|
+
total_sent += sent
|
|
125
|
+
|
|
126
|
+
async def wait_send_all_might_not_block(self) -> None:
|
|
127
|
+
with self._send_conflict_detector:
|
|
128
|
+
if self.socket.fileno() == -1:
|
|
129
|
+
raise trio.ClosedResourceError
|
|
130
|
+
with _translate_socket_errors_to_stream_errors():
|
|
131
|
+
await self.socket.wait_writable()
|
|
132
|
+
|
|
133
|
+
async def send_eof(self) -> None:
|
|
134
|
+
with self._send_conflict_detector:
|
|
135
|
+
await trio.lowlevel.checkpoint()
|
|
136
|
+
# On macOS, calling shutdown a second time raises ENOTCONN, but
|
|
137
|
+
# send_eof needs to be idempotent.
|
|
138
|
+
if self.socket.did_shutdown_SHUT_WR:
|
|
139
|
+
return
|
|
140
|
+
with _translate_socket_errors_to_stream_errors():
|
|
141
|
+
self.socket.shutdown(tsocket.SHUT_WR)
|
|
142
|
+
|
|
143
|
+
async def receive_some(self, max_bytes: int | None = None) -> bytes:
|
|
144
|
+
if max_bytes is None:
|
|
145
|
+
max_bytes = DEFAULT_RECEIVE_SIZE
|
|
146
|
+
if max_bytes < 1:
|
|
147
|
+
raise ValueError("max_bytes must be >= 1")
|
|
148
|
+
with _translate_socket_errors_to_stream_errors():
|
|
149
|
+
return await self.socket.recv(max_bytes)
|
|
150
|
+
|
|
151
|
+
async def aclose(self) -> None:
|
|
152
|
+
self.socket.close()
|
|
153
|
+
await trio.lowlevel.checkpoint()
|
|
154
|
+
|
|
155
|
+
# __aenter__, __aexit__ inherited from HalfCloseableStream are OK
|
|
156
|
+
|
|
157
|
+
@overload
|
|
158
|
+
def setsockopt(self, level: int, option: int, value: int | Buffer) -> None: ...
|
|
159
|
+
|
|
160
|
+
@overload
|
|
161
|
+
def setsockopt(self, level: int, option: int, value: None, length: int) -> None: ...
|
|
162
|
+
|
|
163
|
+
# TODO: rename `length` to `optlen`
|
|
164
|
+
def setsockopt(
|
|
165
|
+
self,
|
|
166
|
+
level: int,
|
|
167
|
+
option: int,
|
|
168
|
+
value: int | Buffer | None,
|
|
169
|
+
length: int | None = None,
|
|
170
|
+
) -> None:
|
|
171
|
+
"""Set an option on the underlying socket.
|
|
172
|
+
|
|
173
|
+
See :meth:`socket.socket.setsockopt` for details.
|
|
174
|
+
|
|
175
|
+
"""
|
|
176
|
+
if length is None:
|
|
177
|
+
if value is None:
|
|
178
|
+
raise TypeError(
|
|
179
|
+
"invalid value for argument 'value', must not be None when specifying length",
|
|
180
|
+
)
|
|
181
|
+
return self.socket.setsockopt(level, option, value)
|
|
182
|
+
if value is not None:
|
|
183
|
+
raise TypeError(
|
|
184
|
+
f"invalid value for argument 'value': {value!r}, must be None when specifying optlen",
|
|
185
|
+
)
|
|
186
|
+
return self.socket.setsockopt(level, option, value, length)
|
|
187
|
+
|
|
188
|
+
@overload
|
|
189
|
+
def getsockopt(self, level: int, option: int) -> int: ...
|
|
190
|
+
|
|
191
|
+
@overload
|
|
192
|
+
def getsockopt(self, level: int, option: int, buffersize: int) -> bytes: ...
|
|
193
|
+
|
|
194
|
+
def getsockopt(self, level: int, option: int, buffersize: int = 0) -> int | bytes:
|
|
195
|
+
"""Check the current value of an option on the underlying socket.
|
|
196
|
+
|
|
197
|
+
See :meth:`socket.socket.getsockopt` for details.
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
# This is to work around
|
|
201
|
+
# https://bitbucket.org/pypy/pypy/issues/2561
|
|
202
|
+
# We should be able to drop it when the next PyPy3 beta is released.
|
|
203
|
+
if buffersize == 0:
|
|
204
|
+
return self.socket.getsockopt(level, option)
|
|
205
|
+
else:
|
|
206
|
+
return self.socket.getsockopt(level, option, buffersize)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
################################################################
|
|
210
|
+
# SocketListener
|
|
211
|
+
################################################################
|
|
212
|
+
|
|
213
|
+
# Accept error handling
|
|
214
|
+
# =====================
|
|
215
|
+
#
|
|
216
|
+
# Literature review
|
|
217
|
+
# -----------------
|
|
218
|
+
#
|
|
219
|
+
# Here's a list of all the possible errors that accept() can return, according
|
|
220
|
+
# to the POSIX spec or the Linux, FreeBSD, macOS, and Windows docs:
|
|
221
|
+
#
|
|
222
|
+
# Can't happen with a Trio socket:
|
|
223
|
+
# - EAGAIN/(WSA)EWOULDBLOCK
|
|
224
|
+
# - EINTR
|
|
225
|
+
# - WSANOTINITIALISED
|
|
226
|
+
# - WSAEINPROGRESS: a blocking call is already in progress
|
|
227
|
+
# - WSAEINTR: someone called WSACancelBlockingCall, but we don't make blocking
|
|
228
|
+
# calls in the first place
|
|
229
|
+
#
|
|
230
|
+
# Something is wrong with our call:
|
|
231
|
+
# - EBADF: not a file descriptor
|
|
232
|
+
# - (WSA)EINVAL: socket isn't listening, or (Linux, BSD) bad flags
|
|
233
|
+
# - (WSA)ENOTSOCK: not a socket
|
|
234
|
+
# - (WSA)EOPNOTSUPP: this kind of socket doesn't support accept
|
|
235
|
+
# - (Linux, FreeBSD, Windows) EFAULT: the sockaddr pointer points to readonly
|
|
236
|
+
# memory
|
|
237
|
+
#
|
|
238
|
+
# Something is wrong with the environment:
|
|
239
|
+
# - (WSA)EMFILE: this process hit its fd limit
|
|
240
|
+
# - ENFILE: the system hit its fd limit
|
|
241
|
+
# - (WSA)ENOBUFS, ENOMEM: unspecified memory problems
|
|
242
|
+
#
|
|
243
|
+
# Something is wrong with the connection we were going to accept. There's a
|
|
244
|
+
# ton of variability between systems here:
|
|
245
|
+
# - ECONNABORTED: documented everywhere, but apparently only the BSDs do this
|
|
246
|
+
# (signals a connection was closed/reset before being accepted)
|
|
247
|
+
# - EPROTO: unspecified protocol error
|
|
248
|
+
# - (Linux) EPERM: firewall rule prevented connection
|
|
249
|
+
# - (Linux) ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH,
|
|
250
|
+
# EOPNOTSUPP, ENETUNREACH, ENOSR, ESOCKTNOSUPPORT, EPROTONOSUPPORT,
|
|
251
|
+
# ETIMEDOUT, ... or any other error that the socket could give, because
|
|
252
|
+
# apparently if an error happens on a connection before it's accept()ed,
|
|
253
|
+
# Linux will report that error from accept().
|
|
254
|
+
# - (Windows) WSAECONNRESET, WSAENETDOWN
|
|
255
|
+
#
|
|
256
|
+
#
|
|
257
|
+
# Code review
|
|
258
|
+
# -----------
|
|
259
|
+
#
|
|
260
|
+
# What do other libraries do?
|
|
261
|
+
#
|
|
262
|
+
# Twisted on Unix or when using nonblocking I/O on Windows:
|
|
263
|
+
# - ignores EPERM, with comment about Linux firewalls
|
|
264
|
+
# - logs and ignores EMFILE, ENOBUFS, ENFILE, ENOMEM, ECONNABORTED
|
|
265
|
+
# Comment notes that ECONNABORTED is a BSDism and that Linux returns the
|
|
266
|
+
# socket before having it fail, and macOS just silently discards it.
|
|
267
|
+
# - other errors are raised, which is logged + kills the socket
|
|
268
|
+
# ref: src/twisted/internet/tcp.py, Port.doRead
|
|
269
|
+
#
|
|
270
|
+
# Twisted using IOCP on Windows:
|
|
271
|
+
# - logs and ignores all errors
|
|
272
|
+
# ref: src/twisted/internet/iocpreactor/tcp.py, Port.handleAccept
|
|
273
|
+
#
|
|
274
|
+
# Tornado:
|
|
275
|
+
# - ignore ECONNABORTED (comments notes that it was observed on FreeBSD)
|
|
276
|
+
# - everything else raised, but all this does (by default) is cause it to be
|
|
277
|
+
# logged and then ignored
|
|
278
|
+
# (ref: tornado/netutil.py, tornado/ioloop.py)
|
|
279
|
+
#
|
|
280
|
+
# libuv on Unix:
|
|
281
|
+
# - ignores ECONNABORTED
|
|
282
|
+
# - does a "trick" for EMFILE or ENFILE
|
|
283
|
+
# - all other errors passed to the connection_cb to be handled
|
|
284
|
+
# (ref: src/unix/stream.c:uv__server_io, uv__emfile_trick)
|
|
285
|
+
#
|
|
286
|
+
# libuv on Windows:
|
|
287
|
+
# src/win/tcp.c:uv_tcp_queue_accept
|
|
288
|
+
# this calls AcceptEx, and then arranges to call:
|
|
289
|
+
# src/win/tcp.c:uv_process_tcp_accept_req
|
|
290
|
+
# this gets the result from AcceptEx. If the original AcceptEx call failed,
|
|
291
|
+
# then "we stop accepting connections and report this error to the
|
|
292
|
+
# connection callback". I think this is for things like ENOTSOCK. If
|
|
293
|
+
# AcceptEx successfully queues an overlapped operation, and then that
|
|
294
|
+
# reports an error, it's just discarded.
|
|
295
|
+
#
|
|
296
|
+
# asyncio, selector mode:
|
|
297
|
+
# - ignores EWOULDBLOCK, EINTR, ECONNABORTED
|
|
298
|
+
# - on EMFILE, ENFILE, ENOBUFS, ENOMEM, logs an error and then disables the
|
|
299
|
+
# listening loop for 1 second
|
|
300
|
+
# - everything else raises, but then the event loop just logs and ignores it
|
|
301
|
+
# (selector_events.py: BaseSelectorEventLoop._accept_connection)
|
|
302
|
+
#
|
|
303
|
+
#
|
|
304
|
+
# What should we do?
|
|
305
|
+
# ------------------
|
|
306
|
+
#
|
|
307
|
+
# When accept() returns an error, we can either ignore it or raise it.
|
|
308
|
+
#
|
|
309
|
+
# We have a long list of errors that should be ignored, and a long list of
|
|
310
|
+
# errors that should be raised. The big question is what to do with an error
|
|
311
|
+
# that isn't on either list. On Linux apparently you can get nearly arbitrary
|
|
312
|
+
# errors from accept() and they should be ignored, because it just indicates a
|
|
313
|
+
# socket that crashed before it began, and there isn't really anything to be
|
|
314
|
+
# done about this, plus on other platforms you may not get any indication at
|
|
315
|
+
# all, so programs have to tolerate not getting any indication too. OTOH if we
|
|
316
|
+
# get an unexpected error then it could indicate something arbitrarily bad --
|
|
317
|
+
# after all, it's unexpected.
|
|
318
|
+
#
|
|
319
|
+
# Given that we know that other libraries seem to be getting along fine with a
|
|
320
|
+
# fairly minimal list of errors to ignore, I think we'll be OK if we write
|
|
321
|
+
# down that list and then raise on everything else.
|
|
322
|
+
#
|
|
323
|
+
# The other question is what to do about the capacity problem errors: EMFILE,
|
|
324
|
+
# ENFILE, ENOBUFS, ENOMEM. Just flat out ignoring these is clearly not optimal
|
|
325
|
+
# -- at the very least you want to log them, and probably you want to take
|
|
326
|
+
# some remedial action. And if we ignore them then it prevents higher levels
|
|
327
|
+
# from doing anything clever with them. So we raise them.
|
|
328
|
+
|
|
329
|
+
_ignorable_accept_errno_names = [
|
|
330
|
+
# Linux can do this when the a connection is denied by the firewall
|
|
331
|
+
"EPERM",
|
|
332
|
+
# BSDs with an early close/reset
|
|
333
|
+
"ECONNABORTED",
|
|
334
|
+
# All the other miscellany noted above -- may not happen in practice, but
|
|
335
|
+
# whatever.
|
|
336
|
+
"EPROTO",
|
|
337
|
+
"ENETDOWN",
|
|
338
|
+
"ENOPROTOOPT",
|
|
339
|
+
"EHOSTDOWN",
|
|
340
|
+
"ENONET",
|
|
341
|
+
"EHOSTUNREACH",
|
|
342
|
+
"EOPNOTSUPP",
|
|
343
|
+
"ENETUNREACH",
|
|
344
|
+
"ENOSR",
|
|
345
|
+
"ESOCKTNOSUPPORT",
|
|
346
|
+
"EPROTONOSUPPORT",
|
|
347
|
+
"ETIMEDOUT",
|
|
348
|
+
"ECONNRESET",
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
# Not all errnos are defined on all platforms
|
|
352
|
+
_ignorable_accept_errnos: set[int] = set()
|
|
353
|
+
for name in _ignorable_accept_errno_names:
|
|
354
|
+
with suppress(AttributeError):
|
|
355
|
+
_ignorable_accept_errnos.add(getattr(errno, name))
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
@final
|
|
359
|
+
class SocketListener(Listener[SocketStream]):
|
|
360
|
+
"""A :class:`~trio.abc.Listener` that uses a listening socket to accept
|
|
361
|
+
incoming connections as :class:`SocketStream` objects.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
socket: The Trio socket object to wrap. Must have type ``SOCK_STREAM``,
|
|
365
|
+
and be listening.
|
|
366
|
+
|
|
367
|
+
Note that the :class:`SocketListener` "takes ownership" of the given
|
|
368
|
+
socket; closing the :class:`SocketListener` will also close the socket.
|
|
369
|
+
|
|
370
|
+
.. attribute:: socket
|
|
371
|
+
|
|
372
|
+
The Trio socket object that this stream wraps.
|
|
373
|
+
|
|
374
|
+
"""
|
|
375
|
+
|
|
376
|
+
def __init__(self, socket: SocketType) -> None:
|
|
377
|
+
if not isinstance(socket, tsocket.SocketType):
|
|
378
|
+
raise TypeError("SocketListener requires a Trio socket object")
|
|
379
|
+
if socket.type != tsocket.SOCK_STREAM:
|
|
380
|
+
raise ValueError("SocketListener requires a SOCK_STREAM socket")
|
|
381
|
+
try:
|
|
382
|
+
listening = socket.getsockopt(tsocket.SOL_SOCKET, tsocket.SO_ACCEPTCONN)
|
|
383
|
+
except OSError:
|
|
384
|
+
# SO_ACCEPTCONN fails on macOS; we just have to trust the user.
|
|
385
|
+
pass
|
|
386
|
+
else:
|
|
387
|
+
if not listening:
|
|
388
|
+
raise ValueError("SocketListener requires a listening socket")
|
|
389
|
+
|
|
390
|
+
self.socket = socket
|
|
391
|
+
|
|
392
|
+
async def accept(self) -> SocketStream:
|
|
393
|
+
"""Accept an incoming connection.
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
:class:`SocketStream`
|
|
397
|
+
|
|
398
|
+
Raises:
|
|
399
|
+
OSError: if the underlying call to ``accept`` raises an unexpected
|
|
400
|
+
error.
|
|
401
|
+
ClosedResourceError: if you already closed the socket.
|
|
402
|
+
|
|
403
|
+
This method handles routine errors like ``ECONNABORTED``, but passes
|
|
404
|
+
other errors on to its caller. In particular, it does *not* make any
|
|
405
|
+
special effort to handle resource exhaustion errors like ``EMFILE``,
|
|
406
|
+
``ENFILE``, ``ENOBUFS``, ``ENOMEM``.
|
|
407
|
+
|
|
408
|
+
"""
|
|
409
|
+
while True:
|
|
410
|
+
try:
|
|
411
|
+
sock, _ = await self.socket.accept()
|
|
412
|
+
except OSError as exc:
|
|
413
|
+
if exc.errno in _closed_stream_errnos:
|
|
414
|
+
raise trio.ClosedResourceError from None
|
|
415
|
+
if exc.errno not in _ignorable_accept_errnos:
|
|
416
|
+
raise
|
|
417
|
+
else:
|
|
418
|
+
return SocketStream(sock)
|
|
419
|
+
|
|
420
|
+
async def aclose(self) -> None:
|
|
421
|
+
"""Close this listener and its underlying socket."""
|
|
422
|
+
self.socket.close()
|
|
423
|
+
await trio.lowlevel.checkpoint()
|