Skip to content

Commit c5655aa

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

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
@@ -590,16 +590,65 @@ def fromshare(info):
590590
return socket(0, 0, 0, info)
591591
__all__.append("fromshare")
592592

593-
if hasattr(_socket, "socketpair"):
593+
# Origin: https://gist.github.com/4325783, by Geert Jansen. Public domain.
594+
# This is used if _socket doesn't natively provide socketpair. It's
595+
# always defined so that it can be patched in for testing purposes.
596+
def _fallback_socketpair(family=AF_INET, type=SOCK_STREAM, proto=0):
597+
if family == AF_INET:
598+
host = _LOCALHOST
599+
elif family == AF_INET6:
600+
host = _LOCALHOST_V6
601+
else:
602+
raise ValueError("Only AF_INET and AF_INET6 socket address families "
603+
"are supported")
604+
if type != SOCK_STREAM:
605+
raise ValueError("Only SOCK_STREAM socket type is supported")
606+
if proto != 0:
607+
raise ValueError("Only protocol zero is supported")
608+
609+
# We create a connected TCP socket. Note the trick with
610+
# setblocking(False) that prevents us from having to create a thread.
611+
lsock = socket(family, type, proto)
612+
try:
613+
lsock.bind((host, 0))
614+
lsock.listen()
615+
# On IPv6, ignore flow_info and scope_id
616+
addr, port = lsock.getsockname()[:2]
617+
csock = socket(family, type, proto)
618+
try:
619+
csock.setblocking(False)
620+
try:
621+
csock.connect((addr, port))
622+
except (BlockingIOError, InterruptedError):
623+
pass
624+
csock.setblocking(True)
625+
ssock, _ = lsock.accept()
626+
except:
627+
csock.close()
628+
raise
629+
finally:
630+
lsock.close()
594631

595-
def socketpair(family=None, type=SOCK_STREAM, proto=0):
596-
"""socketpair([family[, type[, proto]]]) -> (socket object, socket object)
632+
# Authenticating avoids using a connection from something else
633+
# able to connect to {host}:{port} instead of us.
634+
# We expect only AF_INET and AF_INET6 families.
635+
try:
636+
if (
637+
ssock.getsockname() != csock.getpeername()
638+
or csock.getsockname() != ssock.getpeername()
639+
):
640+
raise ConnectionError("Unexpected peer connection")
641+
except:
642+
# getsockname() and getpeername() can fail
643+
# if either socket isn't connected.
644+
ssock.close()
645+
csock.close()
646+
raise
597647

598-
Create a pair of socket objects from the sockets returned by the platform
599-
socketpair() function.
600-
The arguments are the same as for socket() except the default family is
601-
AF_UNIX if defined on the platform; otherwise, the default is AF_INET.
602-
"""
648+
return (ssock, csock)
649+
650+
if hasattr(_socket, "socketpair"):
651+
def socketpair(family=None, type=SOCK_STREAM, proto=0):
603652
if family is None:
604653
try:
605654
family = AF_UNIX
@@ -611,61 +660,7 @@ def socketpair(family=None, type=SOCK_STREAM, proto=0):
611660
return a, b
612661

613662
else:
614-
615-
# Origin: https://gist.github.com/4325783, by Geert Jansen. Public domain.
616-
def socketpair(family=AF_INET, type=SOCK_STREAM, proto=0):
617-
if family == AF_INET:
618-
host = _LOCALHOST
619-
elif family == AF_INET6:
620-
host = _LOCALHOST_V6
621-
else:
622-
raise ValueError("Only AF_INET and AF_INET6 socket address families "
623-
"are supported")
624-
if type != SOCK_STREAM:
625-
raise ValueError("Only SOCK_STREAM socket type is supported")
626-
if proto != 0:
627-
raise ValueError("Only protocol zero is supported")
628-
629-
# We create a connected TCP socket. Note the trick with
630-
# setblocking(False) that prevents us from having to create a thread.
631-
lsock = socket(family, type, proto)
632-
try:
633-
lsock.bind((host, 0))
634-
lsock.listen()
635-
# On IPv6, ignore flow_info and scope_id
636-
addr, port = lsock.getsockname()[:2]
637-
csock = socket(family, type, proto)
638-
try:
639-
csock.setblocking(False)
640-
try:
641-
csock.connect((addr, port))
642-
except (BlockingIOError, InterruptedError):
643-
pass
644-
csock.setblocking(True)
645-
ssock, _ = lsock.accept()
646-
except:
647-
csock.close()
648-
raise
649-
finally:
650-
lsock.close()
651-
652-
# Authenticating avoids using a connection from something else
653-
# able to connect to {host}:{port} instead of us.
654-
# We expect only AF_INET and AF_INET6 families.
655-
try:
656-
if (
657-
ssock.getsockname() != csock.getpeername()
658-
or csock.getsockname() != ssock.getpeername()
659-
):
660-
raise ConnectionError("Unexpected peer connection")
661-
except:
662-
# getsockname() and getpeername() can fail
663-
# if either socket isn't connected.
664-
ssock.close()
665-
csock.close()
666-
raise
667-
668-
return (ssock, csock)
663+
socketpair = _fallback_socketpair
669664
__all__.append("socketpair")
670665

671666
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
@@ -4676,7 +4676,6 @@ def _testSend(self):
46764676

46774677

46784678
class PurePythonSocketPairTest(SocketPairTest):
4679-
46804679
# Explicitly use socketpair AF_INET or AF_INET6 to ensure that is the
46814680
# code path we're using regardless platform is the pure python one where
46824681
# `_socket.socketpair` does not exist. (AF_INET does not work with
@@ -4691,28 +4690,21 @@ def socketpair(self):
46914690
# Local imports in this class make for easy security fix backporting.
46924691

46934692
def setUp(self):
4694-
import _socket
4695-
self._orig_sp = getattr(_socket, 'socketpair', None)
4696-
if self._orig_sp is not None:
4693+
if hasattr(_socket, "socketpair"):
4694+
self._orig_sp = socket.socketpair
46974695
# This forces the version using the non-OS provided socketpair
46984696
# emulation via an AF_INET socket in Lib/socket.py.
4699-
del _socket.socketpair
4700-
import importlib
4701-
global socket
4702-
socket = importlib.reload(socket)
4697+
socket.socketpair = socket._fallback_socketpair
47034698
else:
4704-
pass # This platform already uses the non-OS provided version.
4699+
# This platform already uses the non-OS provided version.
4700+
self._orig_sp = None
47054701
super().setUp()
47064702

47074703
def tearDown(self):
47084704
super().tearDown()
4709-
import _socket
47104705
if self._orig_sp is not None:
47114706
# Restore the default socket.socketpair definition.
4712-
_socket.socketpair = self._orig_sp
4713-
import importlib
4714-
global socket
4715-
socket = importlib.reload(socket)
4707+
socket.socketpair = self._orig_sp
47164708

47174709
def test_recv(self):
47184710
msg = self.serv.recv(1024)

0 commit comments

Comments
 (0)