Skip to content

Commit 3f5d9d1

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

File tree

2 files changed

+64
-77
lines changed

2 files changed

+64
-77
lines changed

Lib/socket.py

Lines changed: 58 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -588,16 +588,65 @@ def fromshare(info):
588588
return socket(0, 0, 0, info)
589589
__all__.append("fromshare")
590590

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

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

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

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

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

Lib/test/test_socket.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4622,7 +4622,6 @@ def _testSend(self):
46224622

46234623

46244624
class PurePythonSocketPairTest(SocketPairTest):
4625-
46264625
# Explicitly use socketpair AF_INET or AF_INET6 to ensure that is the
46274626
# code path we're using regardless platform is the pure python one where
46284627
# `_socket.socketpair` does not exist. (AF_INET does not work with
@@ -4637,28 +4636,21 @@ def socketpair(self):
46374636
# Local imports in this class make for easy security fix backporting.
46384637

46394638
def setUp(self):
4640-
import _socket
4641-
self._orig_sp = getattr(_socket, 'socketpair', None)
4642-
if self._orig_sp is not None:
4639+
if hasattr(_socket, "socketpair"):
4640+
self._orig_sp = socket.socketpair
46434641
# This forces the version using the non-OS provided socketpair
46444642
# emulation via an AF_INET socket in Lib/socket.py.
4645-
del _socket.socketpair
4646-
import importlib
4647-
global socket
4648-
socket = importlib.reload(socket)
4643+
socket.socketpair = socket._fallback_socketpair
46494644
else:
4650-
pass # This platform already uses the non-OS provided version.
4645+
# This platform already uses the non-OS provided version.
4646+
self._orig_sp = None
46514647
super().setUp()
46524648

46534649
def tearDown(self):
46544650
super().tearDown()
4655-
import _socket
46564651
if self._orig_sp is not None:
46574652
# Restore the default socket.socketpair definition.
4658-
_socket.socketpair = self._orig_sp
4659-
import importlib
4660-
global socket
4661-
socket = importlib.reload(socket)
4653+
socket.socketpair = self._orig_sp
46624654

46634655
def test_recv(self):
46644656
msg = self.serv.recv(1024)

0 commit comments

Comments
 (0)