Skip to content

Commit 069bf7e

Browse files
gh-155775: Do not consume the CAPABILITY response in imaplib
It can again be read with response('CAPABILITY') after LOGIN and AUTHENTICATE. The capabilities in the greeting are still consumed. Also document the capabilities attribute. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 0d87a0b commit 069bf7e

4 files changed

Lines changed: 32 additions & 3 deletions

File tree

Doc/library/imaplib.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,20 @@ An :class:`IMAP4` instance has the following methods:
907907

908908
The following attributes are defined on instances of :class:`IMAP4`:
909909

910+
.. attribute:: IMAP4.capabilities
911+
912+
A tuple of the capabilities advertised by the server, in upper case.
913+
914+
It is set when the connection is established,
915+
and refreshed after a successful :meth:`~IMAP4.login`,
916+
:meth:`~IMAP4.authenticate` or :meth:`~IMAP4.starttls`,
917+
because the server can advertise different capabilities
918+
in different connection states.
919+
920+
.. versionchanged:: 3.14.7
921+
Refreshed after :meth:`~IMAP4.login` and :meth:`~IMAP4.authenticate`.
922+
923+
910924
.. attribute:: IMAP4.PROTOCOL_VERSION
911925

912926
The most recent supported protocol in the ``CAPABILITY`` response from the

Lib/imaplib.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ def _connect(self):
366366
self._encoding, 'replace')
367367
raise self.error('invalid greeting: ' + greeting)
368368

369-
self._refresh_capabilities()
369+
# The greeting is not a response to a command.
370+
self._refresh_capabilities(consume=True)
370371
if __debug__:
371372
if self.debug >= 3:
372373
self._mesg('CAPABILITIES: %r' % (self.capabilities,))
@@ -1509,10 +1510,14 @@ def _get_capabilities(self):
15091510
self.capabilities = tuple(dat.split())
15101511

15111512

1512-
def _refresh_capabilities(self):
1513+
def _refresh_capabilities(self, consume=False):
15131514
# Use a CAPABILITY response sent by the server, or ask for it.
1515+
# Unless it is consumed, the response can still be read with
1516+
# response('CAPABILITY').
15141517
if 'CAPABILITY' in self.untagged_responses:
1515-
dat = self.untagged_responses.pop('CAPABILITY')[-1]
1518+
dat = self.untagged_responses['CAPABILITY'][-1]
1519+
if consume:
1520+
del self.untagged_responses['CAPABILITY']
15161521
self.capabilities = tuple(str(dat, self._encoding).upper().split())
15171522
else:
15181523
self._get_capabilities()

Lib/test/test_imaplib.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,8 @@ def cmd_ENABLE(self, tag, args):
10691069
client.login('user', 'pass')
10701070
self.assertIn('ENABLE', client.capabilities)
10711071
self.assertIn('UTF8=ACCEPT', client.capabilities)
1072+
self.assertEqual(client.response('CAPABILITY'),
1073+
('CAPABILITY', [b'IMAP4rev1 ENABLE UTF8=ACCEPT']))
10721074
typ, _ = client.enable('UTF8=ACCEPT')
10731075
self.assertEqual(typ, 'OK')
10741076

@@ -1087,6 +1089,8 @@ def cmd_AUTHENTICATE(self, tag, args):
10871089
self.assertNotIn('ENABLE', client.capabilities)
10881090
client.authenticate('MYAUTH', lambda x: b'fake')
10891091
self.assertIn('ENABLE', client.capabilities)
1092+
self.assertEqual(client.response('CAPABILITY'),
1093+
('CAPABILITY', [b'IMAP4rev1 ENABLE']))
10901094

10911095
def test_greeting_capabilities(self):
10921096
# Capabilities advertised in the greeting are used directly,
@@ -1100,6 +1104,8 @@ def cmd_CAPABILITY(self, tag, args):
11001104
client, server = self._setup(GreetingHandler)
11011105
self.assertEqual(client.capabilities, ('IMAP4REV1', 'ENABLE'))
11021106
self.assertFalse(getattr(server, 'capability_queried', False))
1107+
# The greeting is not a response to a command, so it is consumed.
1108+
self.assertEqual(client.response('CAPABILITY'), ('CAPABILITY', [None]))
11031109

11041110
def test_login_requery_capabilities(self):
11051111
# If the server does not advertise capabilities after login,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix a regression in :mod:`imaplib` introduced in the fix for :gh:`63121`:
2+
refreshing the capabilities after a successful :meth:`~imaplib.IMAP4.login`
3+
or :meth:`~imaplib.IMAP4.authenticate` consumed the ``CAPABILITY`` response,
4+
so it could no longer be read with :meth:`~imaplib.IMAP4.response`.

0 commit comments

Comments
 (0)