https://github.com/python/cpython/commit/89afed25c3427911c9df815ca4d8aeb261ab46ca
commit: 89afed25c3427911c9df815ca4d8aeb261ab46ca
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-07-05T20:02:45+03:00
summary:

gh-77508: Add imaplib.IMAP4.move method (GH-153121)

Add a wrapper for the IMAP MOVE command (RFC 6851), analogous to
copy().  The arguments of MOVE and UID MOVE are quoted when necessary.

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-07-01-15-30-00.gh-issue-77508.Bn2kXt.rst
M Doc/library/imaplib.rst
M Doc/whatsnew/3.16.rst
M Lib/imaplib.py
M Lib/test/test_imaplib.py

diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst
index 3f46c5146acc6ee..6b7c02f54e90af2 100644
--- a/Doc/library/imaplib.rst
+++ b/Doc/library/imaplib.rst
@@ -452,6 +452,15 @@ An :class:`IMAP4` instance has the following methods:
    Returned data are tuples of message part envelope and data.
 
 
+.. method:: IMAP4.move(message_set, new_mailbox)
+
+   Move *message_set* messages onto end of *new_mailbox*.
+
+   The server must support the ``MOVE`` capability (:rfc:`6851`).
+
+   .. versionadded:: next
+
+
 .. method:: IMAP4.myrights(mailbox)
 
    Show my ACLs for a mailbox (i.e. the rights that I have on mailbox).
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 8219700cc1e8385..cf105a26d98d45d 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -228,6 +228,14 @@ io
   (Contributed by Marcel Martin in :gh:`90533`.)
 
 
+imaplib
+-------
+
+* Add the :meth:`~imaplib.IMAP4.move` method,
+  a wrapper for the ``MOVE`` command (:rfc:`6851`).
+  (Contributed by Serhiy Storchaka in :gh:`77508`.)
+
+
 logging
 -------
 
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
index 8e271209a664faa..adfd8afb9c053bb 100644
--- a/Lib/imaplib.py
+++ b/Lib/imaplib.py
@@ -789,6 +789,14 @@ def lsub(self, directory='', pattern='*'):
                                         self._list_mailbox(pattern))
         return self._untagged_response(typ, dat, name)
 
+    def move(self, message_set, new_mailbox):
+        """Move 'message_set' messages onto end of 'new_mailbox'.
+
+        (typ, [data]) = <instance>.move(message_set, new_mailbox)
+        """
+        return self._simple_command('MOVE', self._sequence_set(message_set),
+                                    self._astring(new_mailbox))
+
     def myrights(self, mailbox):
         """Show my ACLs for a mailbox (i.e. the rights that I have on mailbox).
 
@@ -1031,7 +1039,7 @@ def uid(self, command, *args):
                              (command, self.state,
                               ', '.join(Commands[command])))
         name = 'UID'
-        if command == 'COPY':
+        if command in ('COPY', 'MOVE'):
             message_set, new_mailbox = args
             args = (self._sequence_set(message_set),
                     self._astring(new_mailbox))
diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py
index ef283267ff08ef0..046d28f4d30c8a4 100644
--- a/Lib/test/test_imaplib.py
+++ b/Lib/test/test_imaplib.py
@@ -1173,6 +1173,35 @@ def test_uid_copy(self):
         self.assertEqual(data, [None])
         self.assertEqual(server.args, ['COPY', '4827313:4828442', '"New 
folder"'])
 
+    def test_move(self):
+        client, server = self._setup(make_simple_handler('MOVE'))
+        client.login('user', 'pass')
+        client.select()
+        typ, data = client.move('2:4', 'MEETING')
+        self.assertEqual(typ, 'OK')
+        self.assertEqual(data, [b'MOVE completed'])
+        self.assertEqual(server.args, ['2:4', 'MEETING'])
+
+        typ, data = client.move('2:4', 'New folder')
+        self.assertEqual(typ, 'OK')
+        self.assertEqual(data, [b'MOVE completed'])
+        self.assertEqual(server.args, ['2:4', '"New folder"'])
+
+    def test_uid_move(self):
+        client, server = self._setup(make_simple_handler('UID',
+            completed='UID MOVE completed'))
+        client.login('user', 'pass')
+        client.select()
+        typ, data = client.uid('move', '4827313:4828442', 'MEETING')
+        self.assertEqual(typ, 'OK')
+        self.assertEqual(data, [None])
+        self.assertEqual(server.args, ['MOVE', '4827313:4828442', 'MEETING'])
+
+        typ, data = client.uid('move', '4827313:4828442', 'New folder')
+        self.assertEqual(typ, 'OK')
+        self.assertEqual(data, [None])
+        self.assertEqual(server.args, ['MOVE', '4827313:4828442', '"New 
folder"'])
+
     def test_store(self):
         client, server = self._setup(make_simple_handler('STORE', [
             r'* 2 FETCH (FLAGS (\Deleted \Seen))',
diff --git 
a/Misc/NEWS.d/next/Library/2026-07-01-15-30-00.gh-issue-77508.Bn2kXt.rst 
b/Misc/NEWS.d/next/Library/2026-07-01-15-30-00.gh-issue-77508.Bn2kXt.rst
new file mode 100644
index 000000000000000..d6f1bd23be080a3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-01-15-30-00.gh-issue-77508.Bn2kXt.rst
@@ -0,0 +1,2 @@
+Add :meth:`imaplib.IMAP4.move`, a wrapper for the IMAP ``MOVE`` command
+(:rfc:`6851`), analogous to :meth:`~imaplib.IMAP4.copy`.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to