Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f29435f

Browse files
committed
#9559: Append data to single-file mailbox files if messages are only added
If messages were only added, a new file is no longer created and renamed over the old file when flush() is called on an mbox, MMDF or Babyl mailbox.
1 parent 8237258 commit f29435f

3 files changed

Lines changed: 46 additions & 5 deletions

File tree

Lib/mailbox.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,16 +587,19 @@ def __init__(self, path, factory=None, create=True):
587587
self._file = f
588588
self._toc = None
589589
self._next_key = 0
590-
self._pending = False # No changes require rewriting the file.
590+
self._pending = False # No changes require rewriting the file.
591+
self._pending_sync = False # No need to sync the file
591592
self._locked = False
592-
self._file_length = None # Used to record mailbox size
593+
self._file_length = None # Used to record mailbox size
593594

594595
def add(self, message):
595596
"""Add message and return assigned key."""
596597
self._lookup()
597598
self._toc[self._next_key] = self._append_message(message)
598599
self._next_key += 1
599-
self._pending = True
600+
# _append_message appends the message to the mailbox file. We
601+
# don't need a full rewrite + rename, sync is enough.
602+
self._pending_sync = True
600603
return self._next_key - 1
601604

602605
def remove(self, key):
@@ -642,6 +645,11 @@ def unlock(self):
642645
def flush(self):
643646
"""Write any pending changes to disk."""
644647
if not self._pending:
648+
if self._pending_sync:
649+
# Messages have only been added, so syncing the file
650+
# is enough.
651+
_sync_flush(self._file)
652+
self._pending_sync = False
645653
return
646654

647655
# In order to be writing anything out at all, self._toc must
@@ -695,6 +703,7 @@ def flush(self):
695703
self._file = open(self._path, 'rb+')
696704
self._toc = new_toc
697705
self._pending = False
706+
self._pending_sync = False
698707
if self._locked:
699708
_lock_file(self._file, dotlock=False)
700709

@@ -731,6 +740,9 @@ def _append_message(self, message):
731740
"""Append message to mailbox and return (start, stop) offsets."""
732741
self._file.seek(0, 2)
733742
before = self._file.tell()
743+
if len(self._toc) == 0:
744+
# This is the first message
745+
self._pre_mailbox_hook(self._file)
734746
try:
735747
self._pre_message_hook(self._file)
736748
offsets = self._install_message(message)

Lib/test/test_mailbox.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,32 @@ def refreshed():
942942
self._box._refresh()
943943
self.assertTrue(refreshed())
944944

945-
class _TestMboxMMDF(TestMailbox):
945+
946+
class _TestSingleFile(TestMailbox):
947+
'''Common tests for single-file mailboxes'''
948+
949+
def test_add_doesnt_rewrite(self):
950+
# When only adding messages, flush() should not rewrite the
951+
# mailbox file. See issue #9559.
952+
953+
# Inode number changes if the contents are written to another
954+
# file which is then renamed over the original file. So we
955+
# must check that the inode number doesn't change.
956+
inode_before = os.stat(self._path).st_ino
957+
958+
self._box.add(self._template % 0)
959+
self._box.flush()
960+
961+
inode_after = os.stat(self._path).st_ino
962+
self.assertEqual(inode_before, inode_after)
963+
964+
# Make sure the message was really added
965+
self._box.close()
966+
self._box = self._factory(self._path)
967+
self.assertEqual(len(self._box), 1)
968+
969+
970+
class _TestMboxMMDF(_TestSingleFile):
946971

947972
def tearDown(self):
948973
super().tearDown()
@@ -1217,7 +1242,7 @@ def _get_lock_path(self):
12171242
return os.path.join(self._path, '.mh_sequences.lock')
12181243

12191244

1220-
class TestBabyl(TestMailbox, unittest.TestCase):
1245+
class TestBabyl(_TestSingleFile, unittest.TestCase):
12211246

12221247
_factory = lambda self, path, factory=None: mailbox.Babyl(path, factory)
12231248

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ Core and Builtins
8181
Library
8282
-------
8383

84+
- Issue #9559: If messages were only added, a new file is no longer
85+
created and renamed over the old file when flush() is called on an
86+
mbox, MMDF or Babyl mailbox.
87+
8488
- Issue #14653: email.utils.mktime_tz() no longer relies on system
8589
mktime() when timezone offest is supplied.
8690

0 commit comments

Comments
 (0)