Skip to content

Commit 5b7a2cb

Browse files
authored
bpo-34246: Make sure test_smtplib always cleans resources when finished (GH-9108)
* Make sure that when some of the tests in test_smtplib fail, the allocated threads and sockets are not leaked. * Use support.join_thread() instead of thread.join() to avoid infinite blocks.
1 parent d5fbe9b commit 5b7a2cb

File tree

1 file changed

+47
-7
lines changed

1 file changed

+47
-7
lines changed

‎Lib/test/test_smtplib.py‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import unittest
2121
from test import support, mock_socket
2222
from test.support import HOST, HOSTv4, HOSTv6
23+
from test.support import threading_setup, threading_cleanup, join_thread
2324
from unittest.mock import Mock
2425

2526

@@ -193,6 +194,7 @@ class DebuggingServerTests(unittest.TestCase):
193194
maxDiff = None
194195

195196
def setUp(self):
197+
self.thread_key = threading_setup()
196198
self.real_getfqdn = socket.getfqdn
197199
socket.getfqdn = mock_socket.getfqdn
198200
# temporarily replace sys.stdout to capture DebuggingServer output
@@ -224,12 +226,15 @@ def tearDown(self):
224226
self.client_evt.set()
225227
# wait for the server thread to terminate
226228
self.serv_evt.wait()
227-
self.thread.join()
229+
join_thread(self.thread)
228230
# restore sys.stdout
229231
sys.stdout = self.old_stdout
230232
# restore DEBUGSTREAM
231233
smtpd.DEBUGSTREAM.close()
232234
smtpd.DEBUGSTREAM = self.old_DEBUGSTREAM
235+
del self.thread
236+
self.doCleanups()
237+
threading_cleanup(*self.thread_key)
233238

234239
def get_output_without_xpeer(self):
235240
test_output = self.output.getvalue()
@@ -247,6 +252,7 @@ def testSourceAddress(self):
247252
try:
248253
smtp = smtplib.SMTP(self.host, self.port, local_hostname='localhost',
249254
timeout=3, source_address=(self.host, src_port))
255+
self.addCleanup(smtp.close)
250256
self.assertEqual(smtp.source_address, (self.host, src_port))
251257
self.assertEqual(smtp.local_hostname, 'localhost')
252258
smtp.quit()
@@ -257,33 +263,38 @@ def testSourceAddress(self):
257263

258264
def testNOOP(self):
259265
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
266+
self.addCleanup(smtp.close)
260267
expected = (250, b'OK')
261268
self.assertEqual(smtp.noop(), expected)
262269
smtp.quit()
263270

264271
def testRSET(self):
265272
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
273+
self.addCleanup(smtp.close)
266274
expected = (250, b'OK')
267275
self.assertEqual(smtp.rset(), expected)
268276
smtp.quit()
269277

270278
def testELHO(self):
271279
# EHLO isn't implemented in DebuggingServer
272280
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
281+
self.addCleanup(smtp.close)
273282
expected = (250, b'\nSIZE 33554432\nHELP')
274283
self.assertEqual(smtp.ehlo(), expected)
275284
smtp.quit()
276285

277286
def testEXPNNotImplemented(self):
278287
# EXPN isn't implemented in DebuggingServer
279288
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
289+
self.addCleanup(smtp.close)
280290
expected = (502, b'EXPN not implemented')
281291
smtp.putcmd('EXPN')
282292
self.assertEqual(smtp.getreply(), expected)
283293
smtp.quit()
284294

285295
def testVRFY(self):
286296
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
297+
self.addCleanup(smtp.close)
287298
expected = (252, b'Cannot VRFY user, but will accept message ' + \
288299
b'and attempt delivery')
289300
self.assertEqual(smtp.vrfy('[email protected]'), expected)
@@ -294,13 +305,15 @@ def testSecondHELO(self):
294305
# check that a second HELO returns a message that it's a duplicate
295306
# (this behavior is specific to smtpd.SMTPChannel)
296307
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
308+
self.addCleanup(smtp.close)
297309
smtp.helo()
298310
expected = (503, b'Duplicate HELO/EHLO')
299311
self.assertEqual(smtp.helo(), expected)
300312
smtp.quit()
301313

302314
def testHELP(self):
303315
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
316+
self.addCleanup(smtp.close)
304317
self.assertEqual(smtp.help(), b'Supported commands: EHLO HELO MAIL ' + \
305318
b'RCPT DATA RSET NOOP QUIT VRFY')
306319
smtp.quit()
@@ -309,6 +322,7 @@ def testSend(self):
309322
# connect and send mail
310323
m = 'A test message'
311324
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
325+
self.addCleanup(smtp.close)
312326
smtp.sendmail('John', 'Sally', m)
313327
# XXX(nnorwitz): this test is flaky and dies with a bad file descriptor
314328
# in asyncore. This sleep might help, but should really be fixed
@@ -325,6 +339,7 @@ def testSend(self):
325339
def testSendBinary(self):
326340
m = b'A test message'
327341
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
342+
self.addCleanup(smtp.close)
328343
smtp.sendmail('John', 'Sally', m)
329344
# XXX (see comment in testSend)
330345
time.sleep(0.01)
@@ -340,6 +355,7 @@ def testSendNeedingDotQuote(self):
340355
# Issue 12283
341356
m = '.A test\n.mes.sage.'
342357
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
358+
self.addCleanup(smtp.close)
343359
smtp.sendmail('John', 'Sally', m)
344360
# XXX (see comment in testSend)
345361
time.sleep(0.01)
@@ -354,6 +370,7 @@ def testSendNeedingDotQuote(self):
354370
def testSendNullSender(self):
355371
m = 'A test message'
356372
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
373+
self.addCleanup(smtp.close)
357374
smtp.sendmail('<>', 'Sally', m)
358375
# XXX (see comment in testSend)
359376
time.sleep(0.01)
@@ -371,6 +388,7 @@ def testSendNullSender(self):
371388
def testSendMessage(self):
372389
m = email.mime.text.MIMEText('A test message')
373390
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
391+
self.addCleanup(smtp.close)
374392
smtp.send_message(m, from_addr='John', to_addrs='Sally')
375393
# XXX (see comment in testSend)
376394
time.sleep(0.01)
@@ -395,6 +413,7 @@ def testSendMessageWithAddresses(self):
395413
m['CC'] = 'Sally, Fred'
396414
m['Bcc'] = 'John Root <root@localhost>, "Dinsdale" <[email protected]>'
397415
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
416+
self.addCleanup(smtp.close)
398417
smtp.send_message(m)
399418
# XXX (see comment in testSend)
400419
time.sleep(0.01)
@@ -428,6 +447,7 @@ def testSendMessageWithSomeAddresses(self):
428447
m['From'] = '[email protected]'
429448
m['To'] = 'John, Dinsdale'
430449
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
450+
self.addCleanup(smtp.close)
431451
smtp.send_message(m)
432452
# XXX (see comment in testSend)
433453
time.sleep(0.01)
@@ -455,6 +475,7 @@ def testSendMessageWithSpecifiedAddresses(self):
455475
m['From'] = '[email protected]'
456476
m['To'] = 'John, Dinsdale'
457477
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
478+
self.addCleanup(smtp.close)
458479
smtp.send_message(m, from_addr='[email protected]', to_addrs='[email protected]')
459480
# XXX (see comment in testSend)
460481
time.sleep(0.01)
@@ -485,6 +506,7 @@ def testSendMessageWithMultipleFrom(self):
485506
m['Sender'] = '[email protected]'
486507
m['To'] = 'John, Dinsdale'
487508
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
509+
self.addCleanup(smtp.close)
488510
smtp.send_message(m)
489511
# XXX (see comment in testSend)
490512
time.sleep(0.01)
@@ -517,6 +539,7 @@ def testSendMessageResent(self):
517539
m['Resent-To'] = 'Martha <[email protected]>, Jeff'
518540
m['Resent-Bcc'] = '[email protected]'
519541
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
542+
self.addCleanup(smtp.close)
520543
smtp.send_message(m)
521544
# XXX (see comment in testSend)
522545
time.sleep(0.01)
@@ -555,6 +578,7 @@ def testSendMessageMultipleResentRaises(self):
555578
m['Resent-To'] = '[email protected]'
556579
m['Resent-From'] = 'Martha <[email protected]>, Jeff'
557580
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
581+
self.addCleanup(smtp.close)
558582
with self.assertRaises(ValueError):
559583
smtp.send_message(m)
560584
smtp.close()
@@ -630,6 +654,7 @@ class TooLongLineTests(unittest.TestCase):
630654
respdata = b'250 OK' + (b'.' * smtplib._MAXLINE * 2) + b'\n'
631655

632656
def setUp(self):
657+
self.thread_key = threading_setup()
633658
self.old_stdout = sys.stdout
634659
self.output = io.StringIO()
635660
sys.stdout = self.output
@@ -639,15 +664,18 @@ def setUp(self):
639664
self.sock.settimeout(15)
640665
self.port = support.bind_port(self.sock)
641666
servargs = (self.evt, self.respdata, self.sock)
642-
thread = threading.Thread(target=server, args=servargs)
643-
thread.start()
644-
self.addCleanup(thread.join)
667+
self.thread = threading.Thread(target=server, args=servargs)
668+
self.thread.start()
645669
self.evt.wait()
646670
self.evt.clear()
647671

648672
def tearDown(self):
649673
self.evt.wait()
650674
sys.stdout = self.old_stdout
675+
join_thread(self.thread)
676+
del self.thread
677+
self.doCleanups()
678+
threading_cleanup(*self.thread_key)
651679

652680
def testLineTooLong(self):
653681
self.assertRaises(smtplib.SMTPResponseException, smtplib.SMTP,
@@ -877,6 +905,7 @@ def handle_error(self):
877905
class SMTPSimTests(unittest.TestCase):
878906

879907
def setUp(self):
908+
self.thread_key = threading_setup()
880909
self.real_getfqdn = socket.getfqdn
881910
socket.getfqdn = mock_socket.getfqdn
882911
self.serv_evt = threading.Event()
@@ -899,7 +928,10 @@ def tearDown(self):
899928
self.client_evt.set()
900929
# wait for the server thread to terminate
901930
self.serv_evt.wait()
902-
self.thread.join()
931+
join_thread(self.thread)
932+
del self.thread
933+
self.doCleanups()
934+
threading_cleanup(*self.thread_key)
903935

904936
def testBasic(self):
905937
# smoke test
@@ -1162,6 +1194,7 @@ class SMTPUTF8SimTests(unittest.TestCase):
11621194
maxDiff = None
11631195

11641196
def setUp(self):
1197+
self.thread_key = threading_setup()
11651198
self.real_getfqdn = socket.getfqdn
11661199
socket.getfqdn = mock_socket.getfqdn
11671200
self.serv_evt = threading.Event()
@@ -1186,7 +1219,10 @@ def tearDown(self):
11861219
self.client_evt.set()
11871220
# wait for the server thread to terminate
11881221
self.serv_evt.wait()
1189-
self.thread.join()
1222+
join_thread(self.thread)
1223+
del self.thread
1224+
self.doCleanups()
1225+
threading_cleanup(*self.thread_key)
11901226

11911227
def test_test_server_supports_extensions(self):
11921228
smtp = smtplib.SMTP(
@@ -1283,6 +1319,7 @@ class SimSMTPAUTHInitialResponseServer(SimSMTPServer):
12831319

12841320
class SMTPAUTHInitialResponseSimTests(unittest.TestCase):
12851321
def setUp(self):
1322+
self.thread_key = threading_setup()
12861323
self.real_getfqdn = socket.getfqdn
12871324
socket.getfqdn = mock_socket.getfqdn
12881325
self.serv_evt = threading.Event()
@@ -1306,7 +1343,10 @@ def tearDown(self):
13061343
self.client_evt.set()
13071344
# wait for the server thread to terminate
13081345
self.serv_evt.wait()
1309-
self.thread.join()
1346+
join_thread(self.thread)
1347+
del self.thread
1348+
self.doCleanups()
1349+
threading_cleanup(*self.thread_key)
13101350

13111351
def testAUTH_PLAIN_initial_response_login(self):
13121352
self.serv.add_feature('AUTH PLAIN')

0 commit comments

Comments
 (0)