From 98c745a773f0536001d85ebe39372570bb303c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Fran=C3=A7ois=20Natali?= Date: Tue, 14 Oct 2014 21:22:44 +0100 Subject: [PATCH] Issue #18643: Add socket.socketpair() on Windows. --- Doc/library/socket.rst | 4 +++- Lib/socket.py | 51 +++++++++++++++++++++++++++++++++++++++++ Lib/test/test_socket.py | 2 -- Misc/NEWS | 2 ++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index ceeb7767951..1c333f447b0 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -350,7 +350,6 @@ The following functions all create :ref:`socket objects `. type, and protocol number. Address family, socket type, and protocol number are as for the :func:`.socket` function above. The default family is :const:`AF_UNIX` if defined on the platform; otherwise, the default is :const:`AF_INET`. - Availability: Unix. The newly created sockets are :ref:`non-inheritable `. @@ -361,6 +360,9 @@ The following functions all create :ref:`socket objects `. .. versionchanged:: 3.4 The returned sockets are now non-inheritable. + .. versionchanged:: 3.5 + Windows support added. + .. function:: create_connection(address[, timeout[, source_address]]) diff --git a/Lib/socket.py b/Lib/socket.py index 72aa220e6c1..ca3d21cb653 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -76,6 +76,11 @@ SocketType = IntEnum('SocketType', if name.isupper() and name.startswith('SOCK_')}) globals().update(SocketType.__members__) + +_LOCALHOST = '127.0.0.1' +_LOCALHOST_V6 = '::1' + + def _intenum_converter(value, enum_klass): """Convert a numeric family value to an IntEnum member. @@ -468,6 +473,52 @@ if hasattr(_socket, "socketpair"): b = socket(family, type, proto, b.detach()) return a, b +else: + + # Origin: https://gist.github.com/4325783, by Geert Jansen. Public domain. + def socketpair(family=AF_INET, type=SOCK_STREAM, proto=0): + if family == AF_INET: + host = _LOCALHOST + elif family == AF_INET6: + host = _LOCALHOST_V6 + else: + raise ValueError("Only AF_INET and AF_INET6 socket address families " + "are supported") + if type != SOCK_STREAM: + raise ValueError("Only SOCK_STREAM socket type is supported") + if proto != 0: + raise ValueError("Only protocol zero is supported") + + # We create a connected TCP socket. Note the trick with + # setblocking(False) that prevents us from having to create a thread. + lsock = socket(family, type, proto) + try: + lsock.bind((host, 0)) + lsock.listen() + # On IPv6, ignore flow_info and scope_id + addr, port = lsock.getsockname()[:2] + csock = socket(family, type, proto) + try: + csock.setblocking(False) + try: + csock.connect((addr, port)) + except (BlockingIOError, InterruptedError): + pass + csock.setblocking(True) + ssock, _ = lsock.accept() + except: + csock.close() + raise + finally: + lsock.close() + return (ssock, csock) + +socketpair.__doc__ = """socketpair([family[, type[, proto]]]) -> (socket object, socket object) +Create a pair of socket objects from the sockets returned by the platform +socketpair() function. +The arguments are the same as for socket() except the default family is AF_UNIX +if defined on the platform; otherwise, the default is AF_INET. +""" _blocking_errnos = { EAGAIN, EWOULDBLOCK } diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 3f2057bc9e5..ca79532b419 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -3728,8 +3728,6 @@ class TCPCloserTest(ThreadedTCPSocketTest): self.cli.connect((HOST, self.port)) time.sleep(1.0) -@unittest.skipUnless(hasattr(socket, 'socketpair'), - 'test needs socket.socketpair()') @unittest.skipUnless(thread, 'Threading required for this test.') class BasicSocketPairTest(SocketPairTest): diff --git a/Misc/NEWS b/Misc/NEWS index 2691fc6ea08..224b68eddeb 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -177,6 +177,8 @@ Core and Builtins Library ------- +- Issue #18643: Add socket.socketpair() on Windows. + - Issue #22435: Fix a file descriptor leak when SocketServer bind fails. - Issue #13096: Fixed segfault in CTypes POINTER handling of large