Skip to content

Commit 304315d

Browse files
authored
bpo-35347: Cleanup test_socket.NonBlockingTCPTests (GH-10818)
* Replace testInheritFlags() with two tests: testInheritFlagsBlocking() and testInheritFlagsTimeout() to test different default socket timeout. Moreover, the test now checks sock.gettimeout() rather than a functional test on recv(). * Replace time.time() with time.monotonic() * Add socket_setdefaulttimeout() context manager to restore the default timeout when the test completes. * Remove testConnect(): accept() wasn't blocking and testAccept() already tests non-blocking accept(). * Remove accept() functional test from testInitNonBlocking(): already tested by testAccept() * Rewrite testSetBlocking() with a new assert_sock_timeout() method * Use addCleanup() and context manager to close sockets * Replace assertTrue(x < y) with assertLess(x, y)
1 parent ebd5d6d commit 304315d

File tree

1 file changed

+74
-89
lines changed

1 file changed

+74
-89
lines changed

Lib/test/test_socket.py

Lines changed: 74 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
fcntl = None
3535

3636
HOST = support.HOST
37-
MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return
37+
# test unicode string and carriage return
38+
MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8')
3839
MAIN_TIMEOUT = 60.0
3940

4041
VSOCKPORT = 1234
@@ -111,9 +112,14 @@ def _have_socket_vsock():
111112
return ret
112113

113114

114-
def _is_fd_in_blocking_mode(sock):
115-
return not bool(
116-
fcntl.fcntl(sock, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK)
115+
@contextlib.contextmanager
116+
def socket_setdefaulttimeout(timeout):
117+
old_timeout = socket.getdefaulttimeout()
118+
try:
119+
socket.setdefaulttimeout(timeout)
120+
yield
121+
finally:
122+
socket.setdefaulttimeout(old_timeout)
117123

118124

119125
HAVE_SOCKET_CAN = _have_socket_can()
@@ -1069,18 +1075,16 @@ def testDefaultTimeout(self):
10691075
s.close()
10701076

10711077
# Set the default timeout to 10, and see if it propagates
1072-
socket.setdefaulttimeout(10)
1073-
self.assertEqual(socket.getdefaulttimeout(), 10)
1074-
s = socket.socket()
1075-
self.assertEqual(s.gettimeout(), 10)
1076-
s.close()
1078+
with socket_setdefaulttimeout(10):
1079+
self.assertEqual(socket.getdefaulttimeout(), 10)
1080+
with socket.socket() as sock:
1081+
self.assertEqual(sock.gettimeout(), 10)
10771082

1078-
# Reset the default timeout to None, and see if it propagates
1079-
socket.setdefaulttimeout(None)
1080-
self.assertEqual(socket.getdefaulttimeout(), None)
1081-
s = socket.socket()
1082-
self.assertEqual(s.gettimeout(), None)
1083-
s.close()
1083+
# Reset the default timeout to None, and see if it propagates
1084+
socket.setdefaulttimeout(None)
1085+
self.assertEqual(socket.getdefaulttimeout(), None)
1086+
with socket.socket() as sock:
1087+
self.assertEqual(sock.gettimeout(), None)
10841088

10851089
# Check that setting it to an invalid value raises ValueError
10861090
self.assertRaises(ValueError, socket.setdefaulttimeout, -1)
@@ -4218,55 +4222,42 @@ def __init__(self, methodName='runTest'):
42184222
self.event = threading.Event()
42194223
ThreadedTCPSocketTest.__init__(self, methodName=methodName)
42204224

4225+
def assert_sock_timeout(self, sock, timeout):
4226+
self.assertEqual(self.serv.gettimeout(), timeout)
4227+
4228+
blocking = (timeout != 0.0)
4229+
self.assertEqual(sock.getblocking(), blocking)
4230+
4231+
if fcntl is not None:
4232+
# When a Python socket has a non-zero timeout, it's switched
4233+
# internally to a non-blocking mode. Later, sock.sendall(),
4234+
# sock.recv(), and other socket operations use a select() call and
4235+
# handle EWOULDBLOCK/EGAIN on all socket operations. That's how
4236+
# timeouts are enforced.
4237+
fd_blocking = (timeout is None)
4238+
4239+
flag = fcntl.fcntl(sock, fcntl.F_GETFL, os.O_NONBLOCK)
4240+
self.assertEqual(not bool(flag & os.O_NONBLOCK), fd_blocking)
4241+
42214242
def testSetBlocking(self):
4222-
# Testing whether set blocking works
4243+
# Test setblocking() and settimeout() methods
42234244
self.serv.setblocking(True)
4224-
self.assertIsNone(self.serv.gettimeout())
4225-
self.assertTrue(self.serv.getblocking())
4226-
if fcntl:
4227-
self.assertTrue(_is_fd_in_blocking_mode(self.serv))
4245+
self.assert_sock_timeout(self.serv, None)
42284246

42294247
self.serv.setblocking(False)
4230-
self.assertEqual(self.serv.gettimeout(), 0.0)
4231-
self.assertFalse(self.serv.getblocking())
4232-
if fcntl:
4233-
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4248+
self.assert_sock_timeout(self.serv, 0.0)
42344249

42354250
self.serv.settimeout(None)
4236-
self.assertTrue(self.serv.getblocking())
4237-
if fcntl:
4238-
self.assertTrue(_is_fd_in_blocking_mode(self.serv))
4251+
self.assert_sock_timeout(self.serv, None)
42394252

42404253
self.serv.settimeout(0)
4241-
self.assertFalse(self.serv.getblocking())
4242-
self.assertEqual(self.serv.gettimeout(), 0)
4243-
if fcntl:
4244-
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4254+
self.assert_sock_timeout(self.serv, 0)
42454255

42464256
self.serv.settimeout(10)
4247-
self.assertTrue(self.serv.getblocking())
4248-
self.assertEqual(self.serv.gettimeout(), 10)
4249-
if fcntl:
4250-
# When a Python socket has a non-zero timeout, it's
4251-
# switched internally to a non-blocking mode.
4252-
# Later, sock.sendall(), sock.recv(), and other socket
4253-
# operations use a `select()` call and handle EWOULDBLOCK/EGAIN
4254-
# on all socket operations. That's how timeouts are
4255-
# enforced.
4256-
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4257+
self.assert_sock_timeout(self.serv, 10)
42574258

42584259
self.serv.settimeout(0)
4259-
self.assertFalse(self.serv.getblocking())
4260-
if fcntl:
4261-
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4262-
4263-
start = time.time()
4264-
try:
4265-
self.serv.accept()
4266-
except OSError:
4267-
pass
4268-
end = time.time()
4269-
self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.")
4260+
self.assert_sock_timeout(self.serv, 0)
42704261

42714262
def _testSetBlocking(self):
42724263
pass
@@ -4277,8 +4268,10 @@ def testSetBlocking_overflow(self):
42774268
import _testcapi
42784269
if _testcapi.UINT_MAX >= _testcapi.ULONG_MAX:
42794270
self.skipTest('needs UINT_MAX < ULONG_MAX')
4271+
42804272
self.serv.setblocking(False)
42814273
self.assertEqual(self.serv.gettimeout(), 0.0)
4274+
42824275
self.serv.setblocking(_testcapi.UINT_MAX + 1)
42834276
self.assertIsNone(self.serv.gettimeout())
42844277

@@ -4288,50 +4281,51 @@ def testSetBlocking_overflow(self):
42884281
'test needs socket.SOCK_NONBLOCK')
42894282
@support.requires_linux_version(2, 6, 28)
42904283
def testInitNonBlocking(self):
4291-
# reinit server socket
4284+
# create a socket with SOCK_NONBLOCK
42924285
self.serv.close()
4293-
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM |
4294-
socket.SOCK_NONBLOCK)
4295-
self.assertFalse(self.serv.getblocking())
4296-
self.assertEqual(self.serv.gettimeout(), 0)
4297-
self.port = support.bind_port(self.serv)
4298-
self.serv.listen()
4299-
# actual testing
4300-
start = time.time()
4301-
try:
4302-
self.serv.accept()
4303-
except OSError:
4304-
pass
4305-
end = time.time()
4306-
self.assertTrue((end - start) < 1.0, "Error creating with non-blocking mode.")
4286+
self.serv = socket.socket(socket.AF_INET,
4287+
socket.SOCK_STREAM | socket.SOCK_NONBLOCK)
4288+
self.assert_sock_timeout(self.serv, 0)
43074289

43084290
def _testInitNonBlocking(self):
43094291
pass
43104292

4311-
def testInheritFlags(self):
4312-
# Issue #7995: when calling accept() on a listening socket with a
4313-
# timeout, the resulting socket should not be non-blocking.
4314-
self.serv.settimeout(10)
4315-
try:
4293+
def testInheritFlagsBlocking(self):
4294+
# bpo-7995: accept() on a listening socket with a timeout and the
4295+
# default timeout is None, the resulting socket must be blocking.
4296+
with socket_setdefaulttimeout(None):
4297+
self.serv.settimeout(10)
43164298
conn, addr = self.serv.accept()
4317-
message = conn.recv(len(MSG))
4318-
finally:
4319-
conn.close()
4320-
self.serv.settimeout(None)
4299+
self.addCleanup(conn.close)
4300+
self.assertIsNone(conn.gettimeout())
43214301

4322-
def _testInheritFlags(self):
4323-
time.sleep(0.1)
4302+
def _testInheritFlagsBlocking(self):
4303+
self.cli.connect((HOST, self.port))
4304+
4305+
def testInheritFlagsTimeout(self):
4306+
# bpo-7995: accept() on a listening socket with a timeout and the
4307+
# default timeout is None, the resulting socket must inherit
4308+
# the default timeout.
4309+
default_timeout = 20.0
4310+
with socket_setdefaulttimeout(default_timeout):
4311+
self.serv.settimeout(10)
4312+
conn, addr = self.serv.accept()
4313+
self.addCleanup(conn.close)
4314+
self.assertEqual(conn.gettimeout(), default_timeout)
4315+
4316+
def _testInheritFlagsTimeout(self):
43244317
self.cli.connect((HOST, self.port))
4325-
time.sleep(0.5)
4326-
self.cli.send(MSG)
43274318

43284319
def testAccept(self):
43294320
# Testing non-blocking accept
43304321
self.serv.setblocking(0)
43314322

43324323
# connect() didn't start: non-blocking accept() fails
4324+
start_time = time.monotonic()
43334325
with self.assertRaises(BlockingIOError):
43344326
conn, addr = self.serv.accept()
4327+
dt = time.monotonic() - start_time
4328+
self.assertLess(dt, 1.0)
43354329

43364330
self.event.set()
43374331

@@ -4351,15 +4345,6 @@ def _testAccept(self):
43514345

43524346
self.cli.connect((HOST, self.port))
43534347

4354-
def testConnect(self):
4355-
# Testing non-blocking connect
4356-
conn, addr = self.serv.accept()
4357-
conn.close()
4358-
4359-
def _testConnect(self):
4360-
self.cli.settimeout(10)
4361-
self.cli.connect((HOST, self.port))
4362-
43634348
def testRecv(self):
43644349
# Testing non-blocking recv
43654350
conn, addr = self.serv.accept()

0 commit comments

Comments
 (0)