mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
Issue #23972: updates to asyncio datagram API. By Chris Laws.
This commit is contained in:
parent
d17e9785de
commit
b9bf913ab3
7 changed files with 385 additions and 74 deletions
|
@ -700,75 +700,109 @@ class BaseEventLoop(events.AbstractEventLoop):
|
|||
@coroutine
|
||||
def create_datagram_endpoint(self, protocol_factory,
|
||||
local_addr=None, remote_addr=None, *,
|
||||
family=0, proto=0, flags=0):
|
||||
family=0, proto=0, flags=0,
|
||||
reuse_address=None, reuse_port=None,
|
||||
allow_broadcast=None, sock=None):
|
||||
"""Create datagram connection."""
|
||||
if not (local_addr or remote_addr):
|
||||
if family == 0:
|
||||
raise ValueError('unexpected address family')
|
||||
addr_pairs_info = (((family, proto), (None, None)),)
|
||||
else:
|
||||
# join address by (family, protocol)
|
||||
addr_infos = collections.OrderedDict()
|
||||
for idx, addr in ((0, local_addr), (1, remote_addr)):
|
||||
if addr is not None:
|
||||
assert isinstance(addr, tuple) and len(addr) == 2, (
|
||||
'2-tuple is expected')
|
||||
|
||||
infos = yield from self.getaddrinfo(
|
||||
*addr, family=family, type=socket.SOCK_DGRAM,
|
||||
proto=proto, flags=flags)
|
||||
if not infos:
|
||||
raise OSError('getaddrinfo() returned empty list')
|
||||
|
||||
for fam, _, pro, _, address in infos:
|
||||
key = (fam, pro)
|
||||
if key not in addr_infos:
|
||||
addr_infos[key] = [None, None]
|
||||
addr_infos[key][idx] = address
|
||||
|
||||
# each addr has to have info for each (family, proto) pair
|
||||
addr_pairs_info = [
|
||||
(key, addr_pair) for key, addr_pair in addr_infos.items()
|
||||
if not ((local_addr and addr_pair[0] is None) or
|
||||
(remote_addr and addr_pair[1] is None))]
|
||||
|
||||
if not addr_pairs_info:
|
||||
raise ValueError('can not get address information')
|
||||
|
||||
exceptions = []
|
||||
|
||||
for ((family, proto),
|
||||
(local_address, remote_address)) in addr_pairs_info:
|
||||
sock = None
|
||||
if sock is not None:
|
||||
if (local_addr or remote_addr or
|
||||
family or proto or flags or
|
||||
reuse_address or reuse_port or allow_broadcast):
|
||||
# show the problematic kwargs in exception msg
|
||||
opts = dict(local_addr=local_addr, remote_addr=remote_addr,
|
||||
family=family, proto=proto, flags=flags,
|
||||
reuse_address=reuse_address, reuse_port=reuse_port,
|
||||
allow_broadcast=allow_broadcast)
|
||||
problems = ', '.join(
|
||||
'{}={}'.format(k, v) for k, v in opts.items() if v)
|
||||
raise ValueError(
|
||||
'socket modifier keyword arguments can not be used '
|
||||
'when sock is specified. ({})'.format(problems))
|
||||
sock.setblocking(False)
|
||||
r_addr = None
|
||||
try:
|
||||
sock = socket.socket(
|
||||
family=family, type=socket.SOCK_DGRAM, proto=proto)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setblocking(False)
|
||||
|
||||
if local_addr:
|
||||
sock.bind(local_address)
|
||||
if remote_addr:
|
||||
yield from self.sock_connect(sock, remote_address)
|
||||
r_addr = remote_address
|
||||
except OSError as exc:
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
exceptions.append(exc)
|
||||
except:
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
raise
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise exceptions[0]
|
||||
if not (local_addr or remote_addr):
|
||||
if family == 0:
|
||||
raise ValueError('unexpected address family')
|
||||
addr_pairs_info = (((family, proto), (None, None)),)
|
||||
else:
|
||||
# join address by (family, protocol)
|
||||
addr_infos = collections.OrderedDict()
|
||||
for idx, addr in ((0, local_addr), (1, remote_addr)):
|
||||
if addr is not None:
|
||||
assert isinstance(addr, tuple) and len(addr) == 2, (
|
||||
'2-tuple is expected')
|
||||
|
||||
infos = yield from self.getaddrinfo(
|
||||
*addr, family=family, type=socket.SOCK_DGRAM,
|
||||
proto=proto, flags=flags)
|
||||
if not infos:
|
||||
raise OSError('getaddrinfo() returned empty list')
|
||||
|
||||
for fam, _, pro, _, address in infos:
|
||||
key = (fam, pro)
|
||||
if key not in addr_infos:
|
||||
addr_infos[key] = [None, None]
|
||||
addr_infos[key][idx] = address
|
||||
|
||||
# each addr has to have info for each (family, proto) pair
|
||||
addr_pairs_info = [
|
||||
(key, addr_pair) for key, addr_pair in addr_infos.items()
|
||||
if not ((local_addr and addr_pair[0] is None) or
|
||||
(remote_addr and addr_pair[1] is None))]
|
||||
|
||||
if not addr_pairs_info:
|
||||
raise ValueError('can not get address information')
|
||||
|
||||
exceptions = []
|
||||
|
||||
if reuse_address is None:
|
||||
reuse_address = os.name == 'posix' and sys.platform != 'cygwin'
|
||||
|
||||
for ((family, proto),
|
||||
(local_address, remote_address)) in addr_pairs_info:
|
||||
sock = None
|
||||
r_addr = None
|
||||
try:
|
||||
sock = socket.socket(
|
||||
family=family, type=socket.SOCK_DGRAM, proto=proto)
|
||||
if reuse_address:
|
||||
sock.setsockopt(
|
||||
socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
if reuse_port:
|
||||
if not hasattr(socket, 'SO_REUSEPORT'):
|
||||
raise ValueError(
|
||||
'reuse_port not supported by socket module')
|
||||
else:
|
||||
sock.setsockopt(
|
||||
socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
if allow_broadcast:
|
||||
sock.setsockopt(
|
||||
socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
sock.setblocking(False)
|
||||
|
||||
if local_addr:
|
||||
sock.bind(local_address)
|
||||
if remote_addr:
|
||||
yield from self.sock_connect(sock, remote_address)
|
||||
r_addr = remote_address
|
||||
except OSError as exc:
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
exceptions.append(exc)
|
||||
except:
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
raise
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise exceptions[0]
|
||||
|
||||
protocol = protocol_factory()
|
||||
waiter = futures.Future(loop=self)
|
||||
transport = self._make_datagram_transport(sock, protocol, r_addr,
|
||||
waiter)
|
||||
transport = self._make_datagram_transport(
|
||||
sock, protocol, r_addr, waiter)
|
||||
if self._debug:
|
||||
if local_addr:
|
||||
logger.info("Datagram endpoint local_addr=%r remote_addr=%r "
|
||||
|
@ -804,7 +838,8 @@ class BaseEventLoop(events.AbstractEventLoop):
|
|||
sock=None,
|
||||
backlog=100,
|
||||
ssl=None,
|
||||
reuse_address=None):
|
||||
reuse_address=None,
|
||||
reuse_port=None):
|
||||
"""Create a TCP server.
|
||||
|
||||
The host parameter can be a string, in that case the TCP server is bound
|
||||
|
@ -857,8 +892,15 @@ class BaseEventLoop(events.AbstractEventLoop):
|
|||
continue
|
||||
sockets.append(sock)
|
||||
if reuse_address:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
|
||||
True)
|
||||
sock.setsockopt(
|
||||
socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
|
||||
if reuse_port:
|
||||
if not hasattr(socket, 'SO_REUSEPORT'):
|
||||
raise ValueError(
|
||||
'reuse_port not supported by socket module')
|
||||
else:
|
||||
sock.setsockopt(
|
||||
socket.SOL_SOCKET, socket.SO_REUSEPORT, True)
|
||||
# Disable IPv4/IPv6 dual stack support (enabled by
|
||||
# default on Linux) which makes a single socket
|
||||
# listen on both address families.
|
||||
|
|
|
@ -297,7 +297,8 @@ class AbstractEventLoop:
|
|||
|
||||
def create_server(self, protocol_factory, host=None, port=None, *,
|
||||
family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE,
|
||||
sock=None, backlog=100, ssl=None, reuse_address=None):
|
||||
sock=None, backlog=100, ssl=None, reuse_address=None,
|
||||
reuse_port=None):
|
||||
"""A coroutine which creates a TCP server bound to host and port.
|
||||
|
||||
The return value is a Server object which can be used to stop
|
||||
|
@ -327,6 +328,11 @@ class AbstractEventLoop:
|
|||
TIME_WAIT state, without waiting for its natural timeout to
|
||||
expire. If not specified will automatically be set to True on
|
||||
UNIX.
|
||||
|
||||
reuse_port tells the kernel to allow this endpoint to be bound to
|
||||
the same port as other existing endpoints are bound to, so long as
|
||||
they all set this flag when being created. This option is not
|
||||
supported on Windows.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -358,7 +364,37 @@ class AbstractEventLoop:
|
|||
|
||||
def create_datagram_endpoint(self, protocol_factory,
|
||||
local_addr=None, remote_addr=None, *,
|
||||
family=0, proto=0, flags=0):
|
||||
family=0, proto=0, flags=0,
|
||||
reuse_address=None, reuse_port=None,
|
||||
allow_broadcast=None, sock=None):
|
||||
"""A coroutine which creates a datagram endpoint.
|
||||
|
||||
This method will try to establish the endpoint in the background.
|
||||
When successful, the coroutine returns a (transport, protocol) pair.
|
||||
|
||||
protocol_factory must be a callable returning a protocol instance.
|
||||
|
||||
socket family AF_INET or socket.AF_INET6 depending on host (or
|
||||
family if specified), socket type SOCK_DGRAM.
|
||||
|
||||
reuse_address tells the kernel to reuse a local socket in
|
||||
TIME_WAIT state, without waiting for its natural timeout to
|
||||
expire. If not specified it will automatically be set to True on
|
||||
UNIX.
|
||||
|
||||
reuse_port tells the kernel to allow this endpoint to be bound to
|
||||
the same port as other existing endpoints are bound to, so long as
|
||||
they all set this flag when being created. This option is not
|
||||
supported on Windows and some UNIX's. If the
|
||||
:py:data:`~socket.SO_REUSEPORT` constant is not defined then this
|
||||
capability is unsupported.
|
||||
|
||||
allow_broadcast tells the kernel to allow this endpoint to send
|
||||
messages to the broadcast address.
|
||||
|
||||
sock can optionally be specified in order to use a preexisting
|
||||
socket object.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
# Pipes and subprocesses.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue