Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 74125a6

Browse files
authoredApr 15, 2019
bpo-36348: IMAP4.logout() doesn't ignore exc (pythonGH-12411)
The imap.IMAP4.logout() method no longer ignores silently arbitrary exceptions. Changes: * The IMAP4.logout() method now expects a "BYE" untagged response, rather than relying on _check_bye() which raises a self.abort() exception. * IMAP4.__exit__() now does nothing if the client already logged out. * Add more debug info if test_logout() tests fail.
1 parent 0810fa7 commit 74125a6

File tree

5 files changed

+28
-13
lines changed

5 files changed

+28
-13
lines changed
 

‎Doc/library/imaplib.rst

+3
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ An :class:`IMAP4` instance has the following methods:
327327

328328
Shutdown connection to server. Returns server ``BYE`` response.
329329

330+
.. versionchanged:: 3.8
331+
The method no longer ignores silently arbitrary exceptions.
332+
330333

331334
.. method:: IMAP4.lsub(directory='""', pattern='*')
332335

‎Doc/whatsnew/3.8.rst

+3
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,9 @@ Changes in Python behavior
709709
Changes in the Python API
710710
-------------------------
711711

712+
* The :meth:`imap.IMAP4.logout` method no longer ignores silently arbitrary
713+
exceptions.
714+
712715
* The function :func:`platform.popen` has been removed, it was deprecated since
713716
Python 3.3: use :func:`os.popen` instead.
714717

‎Lib/imaplib.py

+16-9
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,9 @@ def __enter__(self):
272272
return self
273273

274274
def __exit__(self, *args):
275+
if self.state == "LOGOUT":
276+
return
277+
275278
try:
276279
self.logout()
277280
except OSError:
@@ -625,11 +628,8 @@ def logout(self):
625628
Returns server 'BYE' response.
626629
"""
627630
self.state = 'LOGOUT'
628-
try: typ, dat = self._simple_command('LOGOUT')
629-
except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]]
631+
typ, dat = self._simple_command('LOGOUT')
630632
self.shutdown()
631-
if 'BYE' in self.untagged_responses:
632-
return 'BYE', self.untagged_responses['BYE']
633633
return typ, dat
634634

635635

@@ -1012,16 +1012,17 @@ def _command(self, name, *args):
10121012

10131013

10141014
def _command_complete(self, name, tag):
1015+
logout = (name == 'LOGOUT')
10151016
# BYE is expected after LOGOUT
1016-
if name != 'LOGOUT':
1017+
if not logout:
10171018
self._check_bye()
10181019
try:
1019-
typ, data = self._get_tagged_response(tag)
1020+
typ, data = self._get_tagged_response(tag, expect_bye=logout)
10201021
except self.abort as val:
10211022
raise self.abort('command: %s => %s' % (name, val))
10221023
except self.error as val:
10231024
raise self.error('command: %s => %s' % (name, val))
1024-
if name != 'LOGOUT':
1025+
if not logout:
10251026
self._check_bye()
10261027
if typ == 'BAD':
10271028
raise self.error('%s command error: %s %s' % (name, typ, data))
@@ -1117,17 +1118,23 @@ def _get_response(self):
11171118
return resp
11181119

11191120

1120-
def _get_tagged_response(self, tag):
1121+
def _get_tagged_response(self, tag, expect_bye=False):
11211122

11221123
while 1:
11231124
result = self.tagged_commands[tag]
11241125
if result is not None:
11251126
del self.tagged_commands[tag]
11261127
return result
11271128

1129+
if expect_bye:
1130+
typ = 'BYE'
1131+
bye = self.untagged_responses.pop(typ, None)
1132+
if bye is not None:
1133+
# Server replies to the "LOGOUT" command with "BYE"
1134+
return (typ, bye)
1135+
11281136
# If we've seen a BYE at this point, the socket will be
11291137
# closed, so report the BYE now.
1130-
11311138
self._check_bye()
11321139

11331140
# Some have reported "unexpected response" exceptions.

‎Lib/test/test_imaplib.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,8 @@ def test_logout(self):
470470
self.assertEqual(typ, 'OK')
471471
self.assertEqual(data[0], b'LOGIN completed')
472472
typ, data = client.logout()
473-
self.assertEqual(typ, 'BYE')
474-
self.assertEqual(data[0], b'IMAP4ref1 Server logging out')
473+
self.assertEqual(typ, 'BYE', (typ, data))
474+
self.assertEqual(data[0], b'IMAP4ref1 Server logging out', (typ, data))
475475
self.assertEqual(client.state, 'LOGOUT')
476476

477477
def test_lsub(self):
@@ -937,7 +937,7 @@ def test_logout(self):
937937
with transient_internet(self.host):
938938
rs = self.server.logout()
939939
self.server = None
940-
self.assertEqual(rs[0], 'BYE')
940+
self.assertEqual(rs[0], 'BYE', rs)
941941

942942

943943
@unittest.skipUnless(ssl, "SSL not available")
@@ -995,7 +995,7 @@ def test_logout(self):
995995
with transient_internet(self.host):
996996
_server = self.imap_class(self.host, self.port)
997997
rs = _server.logout()
998-
self.assertEqual(rs[0], 'BYE')
998+
self.assertEqual(rs[0], 'BYE', rs)
999999

10001000
def test_ssl_context_certfile_exclusive(self):
10011001
with transient_internet(self.host):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The :meth:`imap.IMAP4.logout` method no longer ignores silently arbitrary
2+
exceptions.

0 commit comments

Comments
 (0)
Please sign in to comment.