diff --git a/Lib/mailbox.py b/Lib/mailbox.py index 99426220154360..bc03ca1faf6e6c 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -764,11 +764,7 @@ def flush(self): os.chown(new_file.name, info.st_uid, info.st_gid) except (AttributeError, OSError): pass - try: - os.rename(new_file.name, self._path) - except FileExistsError: - os.remove(self._path) - os.rename(new_file.name, self._path) + os.replace(new_file.name, self._path) self._file = open(self._path, 'rb+') self._toc = new_toc self._pending = False diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 019c699bff55c4..3539ef484c2fb2 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -1,3 +1,4 @@ +import errno import os import sys import time @@ -14,6 +15,7 @@ from test.support import requires_root_user from test.support import socket_helper import unittest +from unittest import mock import textwrap import mailbox import glob @@ -1284,6 +1286,26 @@ class TestMbox(_TestMboxMMDF, unittest.TestCase): _factory = lambda self, path, factory=None: mailbox.mbox(path, factory) + def test_flush_replacement_failure(self): + box = self._box + box.add(self._template % 0) + key = box.add(self._template % 1) + box.flush() + box.remove(key) + + error = OSError(errno.EIO, 'injected replacement failure') + with mock.patch.object(mailbox.os, 'replace', side_effect=error): + with self.assertRaises(OSError) as cm: + box.flush() + self.assertEqual(cm.exception.errno, errno.EIO) + self.assertTrue(os.path.exists(self._path)) + + self._box = mailbox.mbox(self._path) + self.assertEqual( + [message.get_payload() for message in self._box.values()], + ['0\n', '1\n'], + ) + @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()') def test_file_perms(self): # From bug #3228, we want to verify that the mailbox file isn't executable, diff --git a/Misc/NEWS.d/next/Library/2026-08-30-23-29-06.gh-issue-156699.YecuE8.rst b/Misc/NEWS.d/next/Library/2026-08-30-23-29-06.gh-issue-156699.YecuE8.rst new file mode 100644 index 00000000000000..b92498916c69a0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-30-23-29-06.gh-issue-156699.YecuE8.rst @@ -0,0 +1,2 @@ +Preserve :class:`mailbox.mbox`, :class:`mailbox.MMDF`, and +:class:`mailbox.Babyl` files when replacing a rewritten mailbox fails.