Skip to content

Commit c5c4281

Browse files
authored
bpo-40375: Implement imaplib.IMAP4.unselect (GH-19712)
1 parent 91a5ae1 commit c5c4281

File tree

5 files changed

+59
-0
lines changed

5 files changed

+59
-0
lines changed

Doc/library/imaplib.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,15 @@ An :class:`IMAP4` instance has the following methods:
582582

583583
Unsubscribe from old mailbox.
584584

585+
.. method:: IMAP4.unselect()
586+
587+
:meth:`imaplib.IMAP4.unselect` frees server's resources associated with the
588+
selected mailbox and returns the server to the authenticated
589+
state. This command performs the same actions as :meth:`imaplib.IMAP4.close`, except
590+
that no messages are permanently removed from the currently
591+
selected mailbox.
592+
593+
.. versionadded:: 3.9
585594

586595
.. method:: IMAP4.xatom(name[, ...])
587596

Doc/whatsnew/3.9.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,13 @@ with this change. The overridden methods of :class:`~imaplib.IMAP4_SSL` and
320320
:class:`~imaplib.IMAP4_stream` were applied to this change.
321321
(Contributed by Dong-hee Na in :issue:`38615`.)
322322

323+
:meth:`imaplib.IMAP4.unselect` is added.
324+
:meth:`imaplib.IMAP4.unselect` frees server's resources associated with the
325+
selected mailbox and returns the server to the authenticated
326+
state. This command performs the same actions as :meth:`imaplib.IMAP4.close`, except
327+
that no messages are permanently removed from the currently
328+
selected mailbox. (Contributed by Dong-hee Na in :issue:`40375`.)
329+
323330
importlib
324331
---------
325332

Lib/imaplib.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
'THREAD': ('SELECTED',),
9999
'UID': ('SELECTED',),
100100
'UNSUBSCRIBE': ('AUTH', 'SELECTED'),
101+
'UNSELECT': ('SELECTED',),
101102
}
102103

103104
# Patterns to match server responses
@@ -902,6 +903,22 @@ def unsubscribe(self, mailbox):
902903
return self._simple_command('UNSUBSCRIBE', mailbox)
903904

904905

906+
def unselect(self):
907+
"""Free server's resources associated with the selected mailbox
908+
and returns the server to the authenticated state.
909+
This command performs the same actions as CLOSE, except
910+
that no messages are permanently removed from the currently
911+
selected mailbox.
912+
913+
(typ, [data]) = <instance>.unselect()
914+
"""
915+
try:
916+
typ, data = self._simple_command('UNSELECT')
917+
finally:
918+
self.state = 'AUTH'
919+
return typ, data
920+
921+
905922
def xatom(self, name, *args):
906923
"""Allow simple extension commands
907924
notified by server in CAPABILITY response.

Lib/test/test_imaplib.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler):
116116

117117
def setup(self):
118118
super().setup()
119+
self.server.is_selected = False
119120
self.server.logged = None
120121

121122
def _send(self, message):
@@ -190,6 +191,18 @@ def cmd_LOGIN(self, tag, args):
190191
self.server.logged = args[0]
191192
self._send_tagged(tag, 'OK', 'LOGIN completed')
192193

194+
def cmd_SELECT(self, tag, args):
195+
self.server.is_selected = True
196+
self._send_line(b'* 2 EXISTS')
197+
self._send_tagged(tag, 'OK', '[READ-WRITE] SELECT completed.')
198+
199+
def cmd_UNSELECT(self, tag, args):
200+
if self.server.is_selected:
201+
self.server.is_selected = False
202+
self._send_tagged(tag, 'OK', 'Returned to authenticated state. (Success)')
203+
else:
204+
self._send_tagged(tag, 'BAD', 'No mailbox selected')
205+
193206

194207
class NewIMAPTestsMixin():
195208
client = None
@@ -511,6 +524,18 @@ def cmd_LSUB(self, tag, args):
511524
self.assertEqual(typ, 'OK')
512525
self.assertEqual(data[0], b'() "." directoryA')
513526

527+
def test_unselect(self):
528+
client, _ = self._setup(SimpleIMAPHandler)
529+
client.login('user', 'pass')
530+
typ, data = client.select()
531+
self.assertEqual(typ, 'OK')
532+
self.assertEqual(data[0], b'2')
533+
534+
typ, data = client.unselect()
535+
self.assertEqual(typ, 'OK')
536+
self.assertEqual(data[0], b'Returned to authenticated state. (Success)')
537+
self.assertEqual(client.state, 'AUTH')
538+
514539

515540
class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase):
516541
imap_class = imaplib.IMAP4
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:meth:`imaplib.IMAP4.unselect` is added. Patch by Dong-hee Na.

0 commit comments

Comments
 (0)