Skip to content

Commit b8f4163

Browse files
authored
bpo-31233: socketserver.ThreadingMixIn.server_close() (#3523)
socketserver.ThreadingMixIn now keeps a list of non-daemonic threads to wait until all these threads complete in server_close(). Reenable test_logging skipped tests. Fix SocketHandlerTest.tearDown(): close the socket handler before stopping the server, so the server can join threads.
1 parent 97d7e65 commit b8f4163

File tree

3 files changed

+19
-11
lines changed

3 files changed

+19
-11
lines changed

Lib/socketserver.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,9 @@ class ThreadingMixIn:
629629
# Decides how threads will act upon termination of the
630630
# main process
631631
daemon_threads = False
632+
# For non-daemonic threads, list of threading.Threading objects
633+
# used by server_close() to wait for all threads completion.
634+
_threads = None
632635

633636
def process_request_thread(self, request, client_address):
634637
"""Same as in BaseServer but as a thread.
@@ -648,8 +651,20 @@ def process_request(self, request, client_address):
648651
t = threading.Thread(target = self.process_request_thread,
649652
args = (request, client_address))
650653
t.daemon = self.daemon_threads
654+
if not t.daemon:
655+
if self._threads is None:
656+
self._threads = []
657+
self._threads.append(t)
651658
t.start()
652659

660+
def server_close(self):
661+
super().server_close()
662+
threads = self._threads
663+
self._threads = None
664+
if threads:
665+
for thread in threads:
666+
thread.join()
667+
653668

654669
if hasattr(os, "fork"):
655670
class ForkingUDPServer(ForkingMixIn, UDPServer): pass

Lib/test/test_logging.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,7 +1465,6 @@ def test_logger_disabling(self):
14651465
self.assertFalse(logger.disabled)
14661466

14671467

1468-
@unittest.skipIf(True, "FIXME: bpo-30830")
14691468
class SocketHandlerTest(BaseTest):
14701469

14711470
"""Test for SocketHandler objects."""
@@ -1502,11 +1501,11 @@ def setUp(self):
15021501
def tearDown(self):
15031502
"""Shutdown the TCP server."""
15041503
try:
1505-
if self.server:
1506-
self.server.stop(2.0)
15071504
if self.sock_hdlr:
15081505
self.root_logger.removeHandler(self.sock_hdlr)
15091506
self.sock_hdlr.close()
1507+
if self.server:
1508+
self.server.stop(2.0)
15101509
finally:
15111510
BaseTest.tearDown(self)
15121511

@@ -1563,7 +1562,6 @@ def _get_temp_domain_socket():
15631562
os.remove(fn)
15641563
return fn
15651564

1566-
@unittest.skipIf(True, "FIXME: bpo-30830")
15671565
@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
15681566
class UnixSocketHandlerTest(SocketHandlerTest):
15691567

@@ -1581,7 +1579,6 @@ def tearDown(self):
15811579
SocketHandlerTest.tearDown(self)
15821580
support.unlink(self.address)
15831581

1584-
@unittest.skipIf(True, "FIXME: bpo-30830")
15851582
class DatagramHandlerTest(BaseTest):
15861583

15871584
"""Test for DatagramHandler."""
@@ -1646,7 +1643,6 @@ def test_output(self):
16461643
self.handled.wait()
16471644
self.assertEqual(self.log_output, "spam\neggs\n")
16481645

1649-
@unittest.skipIf(True, "FIXME: bpo-30830")
16501646
@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
16511647
class UnixDatagramHandlerTest(DatagramHandlerTest):
16521648

@@ -1731,7 +1727,6 @@ def test_output(self):
17311727
self.handled.wait()
17321728
self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
17331729

1734-
@unittest.skipIf(True, "FIXME: bpo-30830")
17351730
@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
17361731
class UnixSysLogHandlerTest(SysLogHandlerTest):
17371732

@@ -1749,7 +1744,6 @@ def tearDown(self):
17491744
SysLogHandlerTest.tearDown(self)
17501745
support.unlink(self.address)
17511746

1752-
@unittest.skipIf(True, "FIXME: bpo-30830")
17531747
@unittest.skipUnless(support.IPV6_ENABLED,
17541748
'IPv6 support required for this test.')
17551749
class IPv6SysLogHandlerTest(SysLogHandlerTest):
@@ -2872,9 +2866,6 @@ def test_config14_ok(self):
28722866
logging.warning('Exclamation')
28732867
self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
28742868

2875-
# listen() uses ConfigSocketReceiver which is based
2876-
# on socketserver.ThreadingTCPServer
2877-
@unittest.skipIf(True, "FIXME: bpo-30830")
28782869
def setup_via_listener(self, text, verify=None):
28792870
text = text.encode("utf-8")
28802871
# Ask for a randomly assigned port (by using port 0)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
socketserver.ThreadingMixIn now keeps a list of non-daemonic threads to wait
2+
until all these threads complete in server_close().

0 commit comments

Comments
 (0)