diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index dbf4560ab5e53b3..db8ddcefb400590 100644 --- a/Doc/library/imaplib.rst +++ b/Doc/library/imaplib.rst @@ -450,6 +450,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 1a73a79a58b78b1..9b73934bcf03a7e 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 a871e509b0d82c6..1300dc731856425 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -787,6 +787,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]) = .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). @@ -1029,7 +1037,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 cdc2f9c8d852322..f36a786eecfe7e5 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`.