--- Lib/mailbox.py.unified2 2007-01-21 13:48:02.000000000 +0000 +++ Lib/mailbox.py 2007-01-21 21:17:48.000000000 +0000 @@ -558,10 +558,7 @@ if not self._locked: _lock_file(self._file) self._locked = True - if self._pending: - warnings.warn("mailbox .lock() method called with pending changes; " - "should have been locked before making changes", - stacklevel=2) + self._update_toc() def unlock(self): """Unlock the mailbox if it is locked.""" @@ -654,6 +651,7 @@ orig_file.close() if self._locked: _lock_file(self._file, dotlock=False) + self._update_toc() def _pre_mailbox_hook(self, f): """Called before writing the mailbox to file f.""" @@ -677,13 +675,53 @@ def _lookup(self, key=None): """Return (start, stop) or raise KeyError.""" if self._toc is None: - self._generate_toc() + self._update_toc() if key is not None: try: return self._toc[key] except KeyError: raise KeyError('No message with key: %s' % key) + def _update_toc(self): + """Update self._toc and self._file_length from contents of file. + Raise an exception if called with pending changes. + """ + # See bug #1599254 for why this method does what it does. + if self._pending: + raise Error('you must lock the mailbox before making any changes ' + '(_update_toc() was called with self._pending set)') + our_toc = self._toc + next_unused_key = self._next_key + try: + self._toc = None + self._next_key = 0 + self._generate_toc() + new_toc = self._toc + finally: + self._toc = our_toc + self._next_key = next_unused_key + if our_toc is None: + our_toc = {} + old_keys = sorted(our_toc.keys()) + new_keys = sorted(new_toc.keys()) + old_count = len(our_toc) + new_count = len(new_toc) + if new_count < old_count: + for i in range(new_count): + our_toc[old_keys[i]] = new_toc[new_keys[i]] + for key in old_keys[new_count:]: + del our_toc[key] + else: + for i in range(old_count): + our_toc[old_keys[i]] = new_toc[new_keys[i]] + for key in new_keys[old_count:]: + assert self._next_key not in our_toc + our_toc[self._next_key] = new_toc[key] + self._next_key += 1 + self._toc = our_toc + self._file.seek(0, 2) + self._file_length = self._file.tell() + def _append_message(self, message): """Append message to mailbox and return (start, stop) offsets.""" self._file.seek(0, 2) @@ -783,7 +821,6 @@ break self._toc = dict(enumerate(zip(starts, stops))) self._next_key = len(self._toc) - self._file_length = self._file.tell() class MMDF(_mboxMMDF): @@ -827,8 +864,6 @@ break self._toc = dict(enumerate(zip(starts, stops))) self._next_key = len(self._toc) - self._file.seek(0, 2) - self._file_length = self._file.tell() class MH(Mailbox): @@ -1254,8 +1289,6 @@ self._toc = dict(enumerate(zip(starts, stops))) self._labels = dict(enumerate(label_lists)) self._next_key = len(self._toc) - self._file.seek(0, 2) - self._file_length = self._file.tell() def _pre_mailbox_hook(self, f): """Called before writing the mailbox to file f."""