Skip to content

Commit 31302f5

Browse files
miss-islingtonfreakboy3742gpshead
authored
[3.10] gh-122133: Rework pure Python socketpair tests to avoid use of importlib.reload. (GH-122493) (GH-122507)
(cherry picked from commit f071f01) Co-authored-by: Russell Keith-Magee <russell@keith-magee.com> Co-authored-by: Gregory P. Smith <greg@krypto.org>
1 parent 0b65c8b commit 31302f5

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

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

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

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

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

670665
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
@@ -4639,7 +4639,6 @@ def _testSend(self):
46394639

46404640

46414641
class PurePythonSocketPairTest(SocketPairTest):
4642-
46434642
# Explicitly use socketpair AF_INET or AF_INET6 to ensure that is the
46444643
# code path we're using regardless platform is the pure python one where
46454644
# `_socket.socketpair` does not exist. (AF_INET does not work with
@@ -4654,28 +4653,21 @@ def socketpair(self):
46544653
# Local imports in this class make for easy security fix backporting.
46554654

46564655
def setUp(self):
4657-
import _socket
4658-
self._orig_sp = getattr(_socket, 'socketpair', None)
4659-
if self._orig_sp is not None:
4656+
if hasattr(_socket, "socketpair"):
4657+
self._orig_sp = socket.socketpair
46604658
# This forces the version using the non-OS provided socketpair
46614659
# emulation via an AF_INET socket in Lib/socket.py.
4662-
del _socket.socketpair
4663-
import importlib
4664-
global socket
4665-
socket = importlib.reload(socket)
4660+
socket.socketpair = socket._fallback_socketpair
46664661
else:
4667-
pass # This platform already uses the non-OS provided version.
4662+
# This platform already uses the non-OS provided version.
4663+
self._orig_sp = None
46684664
super().setUp()
46694665

46704666
def tearDown(self):
46714667
super().tearDown()
4672-
import _socket
46734668
if self._orig_sp is not None:
46744669
# Restore the default socket.socketpair definition.
4675-
_socket.socketpair = self._orig_sp
4676-
import importlib
4677-
global socket
4678-
socket = importlib.reload(socket)
4670+
socket.socketpair = self._orig_sp
46794671

46804672
def test_recv(self):
46814673
msg = self.serv.recv(1024)

0 commit comments

Comments
 (0)