]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-77508: Add imaplib.IMAP4.move method (GH-153121)
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 5 Jul 2026 17:02:45 +0000 (20:02 +0300)
committerGitHub <noreply@github.com>
Sun, 5 Jul 2026 17:02:45 +0000 (20:02 +0300)
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) <noreply@anthropic.com>
Doc/library/imaplib.rst
Doc/whatsnew/3.16.rst
Lib/imaplib.py
Lib/test/test_imaplib.py
Misc/NEWS.d/next/Library/2026-07-01-15-30-00.gh-issue-77508.Bn2kXt.rst [new file with mode: 0644]

index 3f46c5146acc6eefc9af1291a465de1a3fe01ff8..6b7c02f54e90af2eb09a84ab2d59704d8dfa75fd 100644 (file)
@@ -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).
index 8219700cc1e83854414c7d962183b15761d20df7..cf105a26d98d45df31e5bc7ec9100be53b14b6e2 100644 (file)
@@ -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
 -------
 
index 8e271209a664faa25b2c866b032ef2821e1519a6..adfd8afb9c053bb47cab705fc4d0e997db8e53c6 100644 (file)
@@ -789,6 +789,14 @@ class IMAP4:
                                         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 @@ class IMAP4:
                              (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))
index ef283267ff08ef0c2b3488a71bc745a6a27fe4ca..046d28f4d30c8a47c3ad890b0c7368cb8a64988f 100644 (file)
@@ -1173,6 +1173,35 @@ class NewIMAPTestsMixin:
         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 (file)
index 0000000..d6f1bd2
--- /dev/null
@@ -0,0 +1,2 @@
+Add :meth:`imaplib.IMAP4.move`, a wrapper for the IMAP ``MOVE`` command
+(:rfc:`6851`), analogous to :meth:`~imaplib.IMAP4.copy`.