Skip to content

Commit e319f77

Browse files
miss-islingtonfreakboy3742gpshead
authored
[3.8] gh-122133: Rework pure Python socketpair tests to avoid use of importlib.reload. (GH-122493) (GH-122509)
(cherry picked from commit f071f01) Co-authored-by: Russell Keith-Magee <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]>
1 parent 2621a8a commit e319f77

File tree

2 files changed

+64
-77
lines changed

2 files changed

+64
-77
lines changed

Lib/socket.py

+58-63
Original file line numberDiff line numberDiff line change
@@ -553,16 +553,65 @@ def fromshare(info):
553553
return socket(0, 0, 0, info)
554554
__all__.append("fromshare")
555555

556-
if hasattr(_socket, "socketpair"):
556+
# Origin: https://gist.github.com/4325783, by Geert Jansen. Public domain.
557+
# This is used if _socket doesn't natively provide socketpair. It's
558+
# always defined so that it can be patched in for testing purposes.
559+
def _fallback_socketpair(family=AF_INET, type=SOCK_STREAM, proto=0):
560+
if family == AF_INET:
561+
host = _LOCALHOST
562+
elif family == AF_INET6:
563+
host = _LOCALHOST_V6
564+
else:
565+
raise ValueError("Only AF_INET and AF_INET6 socket address families "
566+
"are supported")
567+
if type != SOCK_STREAM:
568+
raise ValueError("Only SOCK_STREAM socket type is supported")
569+
if proto != 0:
570+
raise ValueError("Only protocol zero is supported")
571+
572+
# We create a connected TCP socket. Note the trick with
573+
# setblocking(False) that prevents us from having to create a thread.
574+
lsock = socket(family, type, proto)
575+
try:
576+
lsock.bind((host, 0))
577+
lsock.listen()
578+
# On IPv6, ignore flow_info and scope_id
579+
addr, port = lsock.getsockname()[:2]
580+
csock = socket(family, type, proto)
581+
try:
582+
csock.setblocking(False)
583+
try:
584+
csock.connect((addr, port))
585+
except (BlockingIOError, InterruptedError):
586+
pass
587+
csock.setblocking(True)
588+
ssock, _ = lsock.accept()
589+
except:
590+
csock.close()
591+
raise
592+
finally:
593+
lsock.close()
557594

558-
def socketpair(family=None, type=SOCK_STREAM, proto=0):
559-
"""socketpair([family[, type[, proto]]]) -> (socket object, socket object)
595+
# Authenticating avoids using a connection from something else
596+
# able to connect to {host}:{port} instead of us.
597+
# We expect only AF_INET and AF_INET6 families.
598+
try:
599+
if (
600+
ssock.getsockname() != csock.getpeername()
601+
or csock.getsockname() != ssock.getpeername()
602+
):
603+
raise ConnectionError("Unexpected peer connection")
604+
except:
605+
# getsockname() and getpeername() can fail
606+
# if either socket isn't connected.
607+
ssock.close()
608+
csock.close()
609+
raise
560610

561-
Create a pair of socket objects from the sockets returned by the platform
562-
socketpair() function.
563-
The arguments are the same as for socket() except the default family is
564-
AF_UNIX if defined on the platform; otherwise, the default is AF_INET.
565-
"""
611+
return (ssock, csock)
612+
613+
if hasattr(_socket, "socketpair"):
614+
def socketpair(family=None, type=SOCK_STREAM, proto=0):
566615
if family is None:
567616
try:
568617
family = AF_UNIX
@@ -574,61 +623,7 @@ def socketpair(family=None, type=SOCK_STREAM, proto=0):
574623
return a, b
575624

576625
else:
577-
578-
# Origin: https://gist.github.com/4325783, by Geert Jansen. Public domain.
579-
def socketpair(family=AF_INET, type=SOCK_STREAM, proto=0):
580-
if family == AF_INET:
581-
host = _LOCALHOST
582-
elif family == AF_INET6:
583-
host = _LOCALHOST_V6
584-
else:
585-
raise ValueError("Only AF_INET and AF_INET6 socket address families "
586-
"are supported")
587-
if type != SOCK_STREAM:
588-
raise ValueError("Only SOCK_STREAM socket type is supported")
589-
if proto != 0:
590-
raise ValueError("Only protocol zero is supported")
591-
592-
# We create a connected TCP socket. Note the trick with
593-
# setblocking(False) that prevents us from having to create a thread.
594-
lsock = socket(family, type, proto)
595-
try:
596-
lsock.bind((host, 0))
597-
lsock.listen()
598-
# On IPv6, ignore flow_info and scope_id
599-
addr, port = lsock.getsockname()[:2]
600-
csock = socket(family, type, proto)
601-
try:
602-
csock.setblocking(False)
603-
try:
604-
csock.connect((addr, port))
605-
except (BlockingIOError, InterruptedError):
606-
pass
607-
csock.setblocking(True)
608-
ssock, _ = lsock.accept()
609-
except:
610-
csock.close()
611-
raise
612-
finally:
613-
lsock.close()
614-
615-
# Authenticating avoids using a connection from something else
616-
# able to connect to {host}:{port} instead of us.
617-
# We expect only AF_INET and AF_INET6 families.
618-
try:
619-
if (
620-
ssock.getsockname() != csock.getpeername()
621-
or csock.getsockname() != ssock.getpeername()
622-
):
623-
raise ConnectionError("Unexpected peer connection")
624-
except:
625-
# getsockname() and getpeername() can fail
626-
# if either socket isn't connected.
627-
ssock.close()
628-
csock.close()
629-
raise
630-
631-
return (ssock, csock)
626+
socketpair = _fallback_socketpair
632627
__all__.append("socketpair")
633628

634629
socketpair.__doc__ = """socketpair([family[, type[, proto]]]) -> (socket object, socket object)

Lib/test/test_socket.py

+6-14
Original file line numberDiff line numberDiff line change
@@ -4316,7 +4316,6 @@ def _testSend(self):
43164316

43174317

43184318
class PurePythonSocketPairTest(SocketPairTest):
4319-
43204319
# Explicitly use socketpair AF_INET or AF_INET6 to ensure that is the
43214320
# code path we're using regardless platform is the pure python one where
43224321
# `_socket.socketpair` does not exist. (AF_INET does not work with
@@ -4331,28 +4330,21 @@ def socketpair(self):
43314330
# Local imports in this class make for easy security fix backporting.
43324331

43334332
def setUp(self):
4334-
import _socket
4335-
self._orig_sp = getattr(_socket, 'socketpair', None)
4336-
if self._orig_sp is not None:
4333+
if hasattr(_socket, "socketpair"):
4334+
self._orig_sp = socket.socketpair
43374335
# This forces the version using the non-OS provided socketpair
43384336
# emulation via an AF_INET socket in Lib/socket.py.
4339-
del _socket.socketpair
4340-
import importlib
4341-
global socket
4342-
socket = importlib.reload(socket)
4337+
socket.socketpair = socket._fallback_socketpair
43434338
else:
4344-
pass # This platform already uses the non-OS provided version.
4339+
# This platform already uses the non-OS provided version.
4340+
self._orig_sp = None
43454341
super().setUp()
43464342

43474343
def tearDown(self):
43484344
super().tearDown()
4349-
import _socket
43504345
if self._orig_sp is not None:
43514346
# Restore the default socket.socketpair definition.
4352-
_socket.socketpair = self._orig_sp
4353-
import importlib
4354-
global socket
4355-
socket = importlib.reload(socket)
4347+
socket.socketpair = self._orig_sp
43564348

43574349
def test_recv(self):
43584350
msg = self.serv.recv(1024)

0 commit comments

Comments
 (0)