From 89afed25c3427911c9df815ca4d8aeb261ab46ca Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 5 Jul 2026 20:02:45 +0300 Subject: [PATCH] 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) --- Doc/library/imaplib.rst | 9 ++++++ Doc/whatsnew/3.16.rst | 8 +++++ Lib/imaplib.py | 10 ++++++- Lib/test/test_imaplib.py | 29 +++++++++++++++++++ ...6-07-01-15-30-00.gh-issue-77508.Bn2kXt.rst | 2 ++ 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-01-15-30-00.gh-issue-77508.Bn2kXt.rst diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index 3f46c5146acc..6b7c02f54e90 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 8219700cc1e8..cf105a26d98d 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 8e271209a664..adfd8afb9c05 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -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]) = .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)) diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index ef283267ff08..046d28f4d30c 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -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 index 000000000000..d6f1bd23be08 --- /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`. -- 2.47.3