Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add typing for _highlevel_open_tcp_listeners.py #2724

Merged
merged 27 commits into from
Aug 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8370724
Add typing for `_highlevel_open_tcp_listeners.py`
CoolCat467 Jul 29, 2023
0ea61fb
Changes from `./check.sh`
CoolCat467 Jul 29, 2023
553b1bc
Revert "Changes from `./check.sh`"
CoolCat467 Jul 30, 2023
cdec244
Update `veryfy_types.json`
CoolCat467 Jul 30, 2023
dddeca0
Update `verify_types.json`
CoolCat467 Jul 30, 2023
a3f1a0d
Remove unused `Iterable` import
CoolCat467 Jul 30, 2023
a494931
Change ... to comma
CoolCat467 Jul 30, 2023
43d229c
`Task` -> `TaskStatus`
CoolCat467 Jul 30, 2023
b1d12cf
Merge branch 'master' into typing-highlevel-tcp
CoolCat467 Jul 30, 2023
cf4a35b
Fix double export of `TaskStatus`
CoolCat467 Jul 30, 2023
069fb8b
Update `verify_types.json`
CoolCat467 Jul 31, 2023
e90e4cc
Accept `math.inf`
CoolCat467 Jul 31, 2023
61a8024
Merge branch 'master' into typing-highlevel-tcp
CoolCat467 Jul 31, 2023
4050ca6
Update `verify_types.json`
CoolCat467 Jul 31, 2023
b305a4a
Update `test_highlevel_open_tcp_listeners.py`
CoolCat467 Aug 1, 2023
ebc1549
Merge branch 'master' into typing-highlevel-tcp
CoolCat467 Aug 1, 2023
ce614c4
Fix formatting
CoolCat467 Aug 1, 2023
fbd8245
Update `verify_types.json`
CoolCat467 Aug 1, 2023
79b478d
Update `test_highlevel_open_tcp_listeners.py`
CoolCat467 Aug 1, 2023
d71cedc
Attempt to fix readthedocs error
CoolCat467 Aug 1, 2023
e4ae5a6
Merge branch 'typing-highlevel-tcp' of https://github.com/CoolCat467/…
CoolCat467 Aug 1, 2023
e61b582
Change type imports
CoolCat467 Aug 1, 2023
17ec512
Only import `TaskStatus` if type checking
CoolCat467 Aug 2, 2023
4a9b024
Revert "Only import `TaskStatus` if type checking"
CoolCat467 Aug 2, 2023
faeebad
Merge branch 'master' into typing-highlevel-tcp
CoolCat467 Aug 2, 2023
6ff13c4
Remove mypy extension warning comment
CoolCat467 Aug 3, 2023
2ab89e4
Merge branch 'master' into typing-highlevel-tcp
CoolCat467 Aug 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -922,9 +922,6 @@ The nursery API

See :meth:`~Nursery.start`.

.. autoclass:: TaskStatus
:members:

.. _task-local-storage:

Task-local storage
Expand Down
2 changes: 2 additions & 0 deletions docs/source/reference-lowlevel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,8 @@ Task API
putting a task to sleep and then waking it up again. (See
:func:`wait_task_rescheduled` for details.)

.. autoclass:: TaskStatus
:members:

.. _guest-mode:

Expand Down
1 change: 0 additions & 1 deletion trio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
EndOfChannel as EndOfChannel,
Nursery as Nursery,
RunFinishedError as RunFinishedError,
TaskStatus as TaskStatus,
TrioInternalError as TrioInternalError,
WouldBlock as WouldBlock,
current_effective_deadline as current_effective_deadline,
Expand Down
4 changes: 2 additions & 2 deletions trio/_dtls.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
from OpenSSL.SSL import Context
from typing_extensions import Self, TypeAlias

from ._core._run import TaskStatus
from ._socket import Address, _SocketType
from trio.lowlevel import TaskStatus
from trio.socket import Address, _SocketType

MAX_UDP_PACKET_SIZE = 65527

Expand Down
49 changes: 32 additions & 17 deletions trio/_highlevel_open_tcp_listeners.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from __future__ import annotations

import errno
import sys
from collections.abc import Awaitable, Callable
from math import inf

import trio
from trio.lowlevel import TaskStatus

from . import socket as tsocket

Expand All @@ -23,7 +27,7 @@
# backpressure. If a connection gets stuck waiting in the backlog queue, then
# from the peer's point of view the connection succeeded but then their
# send/recv will stall until we get to it, possibly for a long time. OTOH if
# there isn't room in the backlog queue... then their connect stalls, possibly
# there isn't room in the backlog queue, then their connect stalls, possibly
# for a long time, which is pretty much the same thing.
#
# A large backlog can also use a bit more kernel memory, but this seems fairly
Expand All @@ -37,16 +41,24 @@
# so this is unnecessary -- we can just pass in "infinity" and get the maximum
# that way. (Verified on Windows, Linux, macOS using
# notes-to-self/measure-listen-backlog.py)
def _compute_backlog(backlog):
if backlog is None:
backlog = inf
def _compute_backlog(backlog: int | float | None) -> int:
# Many systems (Linux, BSDs, ...) store the backlog in a uint16 and are
# missing overflow protection, so we apply our own overflow protection.
# https://github.com/golang/go/issues/5030
if isinstance(backlog, float):
# TODO: Remove when removing infinity support
# https://github.com/python-trio/trio/pull/2724#discussion_r1278541729
if backlog != inf:
raise ValueError(f"Only accepts infinity, not {backlog!r}")
CoolCat467 marked this conversation as resolved.
Show resolved Hide resolved
backlog = None
if backlog is None:
return 0xFFFF
return min(backlog, 0xFFFF)


async def open_tcp_listeners(port, *, host=None, backlog=None):
async def open_tcp_listeners(
port: int, *, host: str | bytes | None = None, backlog: int | float | None = None
) -> list[trio.SocketListener]:
"""Create :class:`SocketListener` objects to listen for TCP connections.

Args:
Expand All @@ -62,7 +74,7 @@ async def open_tcp_listeners(port, *, host=None, backlog=None):
:func:`open_tcp_listeners` will bind to both the IPv4 wildcard
address (``0.0.0.0``) and also the IPv6 wildcard address (``::``).

host (str, bytes-like, or None): The local interface to bind to. This is
host (str, bytes, or None): The local interface to bind to. This is
A5rocks marked this conversation as resolved.
Show resolved Hide resolved
passed to :func:`~socket.getaddrinfo` with the ``AI_PASSIVE`` flag
set.

Expand All @@ -78,13 +90,16 @@ async def open_tcp_listeners(port, *, host=None, backlog=None):
all interfaces, pass the family-specific wildcard address:
``"0.0.0.0"`` for IPv4-only and ``"::"`` for IPv6-only.

backlog (int or None): The listen backlog to use. If you leave this as
``None`` then Trio will pick a good default. (Currently: whatever
backlog (int, math.inf, or None): The listen backlog to use. If you leave this as
``None`` or ``math.inf`` then Trio will pick a good default. (Currently: whatever
your system has configured as the maximum backlog.)

Returns:
list of :class:`SocketListener`

Raises:
:class:`TypeError` if invalid arguments.

"""
# getaddrinfo sometimes allows port=None, sometimes not (depending on
# whether host=None). And on some systems it treats "" as 0, others it
Expand All @@ -93,7 +108,7 @@ async def open_tcp_listeners(port, *, host=None, backlog=None):
if not isinstance(port, int):
raise TypeError(f"port must be an int not {port!r}")

backlog = _compute_backlog(backlog)
computed_backlog = _compute_backlog(backlog)

addresses = await tsocket.getaddrinfo(
host, port, type=tsocket.SOCK_STREAM, flags=tsocket.AI_PASSIVE
Expand Down Expand Up @@ -126,7 +141,7 @@ async def open_tcp_listeners(port, *, host=None, backlog=None):
sock.setsockopt(tsocket.IPPROTO_IPV6, tsocket.IPV6_V6ONLY, 1)

await sock.bind(sockaddr)
sock.listen(backlog)
sock.listen(computed_backlog)

listeners.append(trio.SocketListener(sock))
except:
Expand All @@ -150,14 +165,14 @@ async def open_tcp_listeners(port, *, host=None, backlog=None):


async def serve_tcp(
handler,
port,
handler: Callable[[trio.SocketStream], Awaitable[object]],
port: int,
*,
host=None,
backlog=None,
handler_nursery=None,
task_status=trio.TASK_STATUS_IGNORED,
):
host: str | bytes | None = None,
backlog: int | float | None = None,
handler_nursery: trio.Nursery | None = None,
task_status: TaskStatus = trio.TASK_STATUS_IGNORED, # type: ignore[assignment] # default has type "_TaskStatusIgnored", argument has type "TaskStatus"
) -> None:
"""Listen for incoming TCP connections, and for each one start a task
running ``handler(stream)``.

Expand Down
12 changes: 12 additions & 0 deletions trio/_tests/test_highlevel_open_tcp_listeners.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import errno
import socket as stdlib_socket
import sys
from math import inf

import attr
import pytest
Expand Down Expand Up @@ -289,6 +290,7 @@ async def test_open_tcp_listeners_backlog():
tsocket.set_custom_socket_factory(fsf)
for given, expected in [
(None, 0xFFFF),
(inf, 0xFFFF),
(99999999, 0xFFFF),
(10, 10),
(1, 1),
Expand All @@ -297,3 +299,13 @@ async def test_open_tcp_listeners_backlog():
assert listeners
for listener in listeners:
assert listener.socket.backlog == expected


async def test_open_tcp_listeners_backlog_float_error():
fsf = FakeSocketFactory(99)
tsocket.set_custom_socket_factory(fsf)
for should_fail in (0.0, 2.18, 3.14, 9.75):
with pytest.raises(
ValueError, match=f"Only accepts infinity, not {should_fail!r}"
):
await open_tcp_listeners(0, backlog=should_fail)
7 changes: 3 additions & 4 deletions trio/_tests/verify_types.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
"warningCount": 0
},
"typeCompleteness": {
"completenessScore": 0.9154704944178629,
"completenessScore": 0.9170653907496013,
"exportedSymbolCounts": {
"withAmbiguousType": 0,
"withKnownType": 574,
"withUnknownType": 53
"withKnownType": 575,
"withUnknownType": 52
},
"ignoreUnknownTypesFromImports": true,
"missingClassDocStringCount": 1,
Expand Down Expand Up @@ -108,7 +108,6 @@
"trio.lowlevel.wait_writable",
"trio.open_ssl_over_tcp_listeners",
"trio.open_ssl_over_tcp_stream",
"trio.open_tcp_listeners",
"trio.open_unix_socket",
"trio.run",
"trio.run_process",
Expand Down
1 change: 1 addition & 0 deletions trio/lowlevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
RaiseCancelT as RaiseCancelT,
RunVar as RunVar,
Task as Task,
TaskStatus as TaskStatus,
TrioToken as TrioToken,
UnboundedQueue as UnboundedQueue,
UnboundedQueueStatistics as UnboundedQueueStatistics,
Expand Down