Skip to content

Commit a03379a

Browse files
committed
Merge 3.5 (asyncio)
2 parents b2e4b27 + cbaa35d commit a03379a

5 files changed

Lines changed: 74 additions & 11 deletions

File tree

Doc/library/asyncio-eventloop.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,12 @@ Creating listening connections
333333

334334
Parameters:
335335

336-
* If *host* is an empty string or ``None``, all interfaces are assumed
337-
and a list of multiple sockets will be returned (most likely
338-
one for IPv4 and another one for IPv6).
336+
* The *host* parameter can be a string, in that case the TCP server is
337+
bound to *host* and *port*. The *host* parameter can also be a sequence
338+
of strings and in that case the TCP server is bound to all hosts of the
339+
sequence. If *host* is an empty string or ``None``, all interfaces are
340+
assumed and a list of multiple sockets will be returned (most likely one
341+
for IPv4 and another one for IPv6).
339342

340343
* *family* can be set to either :data:`socket.AF_INET` or
341344
:data:`~socket.AF_INET6` to force the socket to use IPv4 or IPv6. If not set
@@ -369,6 +372,10 @@ Creating listening connections
369372
The function :func:`start_server` creates a (:class:`StreamReader`,
370373
:class:`StreamWriter`) pair and calls back a function with this pair.
371374

375+
.. versionchanged:: 3.5.1
376+
377+
The *host* parameter can now be a sequence of strings.
378+
372379

373380
.. coroutinemethod:: BaseEventLoop.create_unix_server(protocol_factory, path=None, \*, sock=None, backlog=100, ssl=None)
374381

Lib/asyncio/base_events.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import concurrent.futures
1919
import heapq
2020
import inspect
21+
import itertools
2122
import logging
2223
import os
2324
import socket
@@ -786,6 +787,15 @@ def create_datagram_endpoint(self, protocol_factory,
786787

787788
return transport, protocol
788789

790+
@coroutine
791+
def _create_server_getaddrinfo(self, host, port, family, flags):
792+
infos = yield from self.getaddrinfo(host, port, family=family,
793+
type=socket.SOCK_STREAM,
794+
flags=flags)
795+
if not infos:
796+
raise OSError('getaddrinfo({!r}) returned empty list'.format(host))
797+
return infos
798+
789799
@coroutine
790800
def create_server(self, protocol_factory, host=None, port=None,
791801
*,
@@ -795,7 +805,13 @@ def create_server(self, protocol_factory, host=None, port=None,
795805
backlog=100,
796806
ssl=None,
797807
reuse_address=None):
798-
"""Create a TCP server bound to host and port.
808+
"""Create a TCP server.
809+
810+
The host parameter can be a string, in that case the TCP server is bound
811+
to host and port.
812+
813+
The host parameter can also be a sequence of strings and in that case
814+
the TCP server is bound to all hosts of the sequence.
799815
800816
Return a Server object which can be used to stop the service.
801817
@@ -813,13 +829,18 @@ def create_server(self, protocol_factory, host=None, port=None,
813829
reuse_address = os.name == 'posix' and sys.platform != 'cygwin'
814830
sockets = []
815831
if host == '':
816-
host = None
832+
hosts = [None]
833+
elif (isinstance(host, str) or
834+
not isinstance(host, collections.Iterable)):
835+
hosts = [host]
836+
else:
837+
hosts = host
817838

818-
infos = yield from self.getaddrinfo(
819-
host, port, family=family,
820-
type=socket.SOCK_STREAM, proto=0, flags=flags)
821-
if not infos:
822-
raise OSError('getaddrinfo() returned empty list')
839+
fs = [self._create_server_getaddrinfo(host, port, family=family,
840+
flags=flags)
841+
for host in hosts]
842+
infos = yield from tasks.gather(*fs, loop=self)
843+
infos = itertools.chain.from_iterable(infos)
823844

824845
completed = False
825846
try:

Lib/asyncio/events.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,8 @@ def create_server(self, protocol_factory, host=None, port=None, *,
305305
306306
If host is an empty string or None all interfaces are assumed
307307
and a list of multiple sockets will be returned (most likely
308-
one for IPv4 and another one for IPv6).
308+
one for IPv4 and another one for IPv6). The host parameter can also be a
309+
sequence (e.g. list) of hosts to bind to.
309310
310311
family can be set to either AF_INET or AF_INET6 to force the
311312
socket to use IPv4 or IPv6. If not set it will be determined

Lib/test/test_asyncio/test_events.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,39 @@ def test_create_connection_local_addr_in_use(self):
745745
self.assertEqual(cm.exception.errno, errno.EADDRINUSE)
746746
self.assertIn(str(httpd.address), cm.exception.strerror)
747747

748+
@mock.patch('asyncio.base_events.socket')
749+
def create_server_multiple_hosts(self, family, hosts, mock_sock):
750+
@asyncio.coroutine
751+
def getaddrinfo(host, port, *args, **kw):
752+
if family == socket.AF_INET:
753+
return [[family, socket.SOCK_STREAM, 6, '', (host, port)]]
754+
else:
755+
return [[family, socket.SOCK_STREAM, 6, '', (host, port, 0, 0)]]
756+
757+
def getaddrinfo_task(*args, **kwds):
758+
return asyncio.Task(getaddrinfo(*args, **kwds), loop=self.loop)
759+
760+
if family == socket.AF_INET:
761+
mock_sock.socket().getsockbyname.side_effect = [(host, 80)
762+
for host in hosts]
763+
else:
764+
mock_sock.socket().getsockbyname.side_effect = [(host, 80, 0, 0)
765+
for host in hosts]
766+
self.loop.getaddrinfo = getaddrinfo_task
767+
self.loop._start_serving = mock.Mock()
768+
f = self.loop.create_server(lambda: MyProto(self.loop), hosts, 80)
769+
server = self.loop.run_until_complete(f)
770+
self.addCleanup(server.close)
771+
server_hosts = [sock.getsockbyname()[0] for sock in server.sockets]
772+
self.assertEqual(server_hosts, hosts)
773+
774+
def test_create_server_multiple_hosts_ipv4(self):
775+
self.create_server_multiple_hosts(socket.AF_INET,
776+
['1.2.3.4', '5.6.7.8'])
777+
778+
def test_create_server_multiple_hosts_ipv6(self):
779+
self.create_server_multiple_hosts(socket.AF_INET6, ['::1', '::2'])
780+
748781
def test_create_server(self):
749782
proto = MyProto(self.loop)
750783
f = self.loop.create_server(lambda: proto, '0.0.0.0', 0)

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,7 @@ Adam Simpkins
13321332
Ravi Sinha
13331333
Janne Sinkkonen
13341334
Ng Pheng Siong
1335+
Yann Sionneau
13351336
George Sipe
13361337
J. Sipprell
13371338
Kragen Sitaker

0 commit comments

Comments
 (0)