diff options
author | Petri Lehtinen <petri@digip.org> | 2012-09-25 19:00:32 (GMT) |
---|---|---|
committer | Petri Lehtinen <petri@digip.org> | 2012-09-25 19:02:06 (GMT) |
commit | f39884bb5a01c3fa9db74833d2d8a05bf0530315 (patch) | |
tree | f79b3eeaaac3713727c6e7c6f5d1828d571e47e3 /Lib/mailbox.py | |
parent | 468091954f9e9d46e9d4c7673a37ccb3b3b03eb5 (diff) | |
download | cpython-f39884bb5a01c3fa9db74833d2d8a05bf0530315.zip cpython-f39884bb5a01c3fa9db74833d2d8a05bf0530315.tar.gz cpython-f39884bb5a01c3fa9db74833d2d8a05bf0530315.tar.bz2 |
#15222: Insert blank line after each message in mbox mailboxes
Diffstat (limited to 'Lib/mailbox.py')
-rw-r--r-- | Lib/mailbox.py | 44 |
1 files changed, 38 insertions, 6 deletions
diff --git a/Lib/mailbox.py b/Lib/mailbox.py index 282c055..c73fb95 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -208,6 +208,9 @@ class Mailbox: raise ValueError("String input must be ASCII-only; " "use bytes or a Message instead") + # Whether each message must end in a newline + _append_newline = False + def _dump_message(self, message, target, mangle_from_=False): # This assumes the target file is open in binary mode. """Dump message contents to target file.""" @@ -219,6 +222,9 @@ class Mailbox: data = buffer.read() data = data.replace(b'\n', linesep) target.write(data) + if self._append_newline and not data.endswith(linesep): + # Make sure the message ends with a newline + target.write(linesep) elif isinstance(message, (str, bytes, io.StringIO)): if isinstance(message, io.StringIO): warnings.warn("Use of StringIO input is deprecated, " @@ -230,11 +236,15 @@ class Mailbox: message = message.replace(b'\nFrom ', b'\n>From ') message = message.replace(b'\n', linesep) target.write(message) + if self._append_newline and not message.endswith(linesep): + # Make sure the message ends with a newline + target.write(linesep) elif hasattr(message, 'read'): if hasattr(message, 'buffer'): warnings.warn("Use of text mode files is deprecated, " "use a binary mode file instead", DeprecationWarning, 3) message = message.buffer + lastline = None while True: line = message.readline() # Universal newline support. @@ -248,6 +258,10 @@ class Mailbox: line = b'>From ' + line[5:] line = line.replace(b'\n', linesep) target.write(line) + lastline = line + if self._append_newline and lastline and not lastline.endswith(linesep): + # Make sure the message ends with a newline + target.write(linesep) else: raise TypeError('Invalid message type: %s' % type(message)) @@ -833,30 +847,48 @@ class mbox(_mboxMMDF): _mangle_from_ = True + # All messages must end in a newline character, and + # _post_message_hooks outputs an empty line between messages. + _append_newline = True + def __init__(self, path, factory=None, create=True): """Initialize an mbox mailbox.""" self._message_factory = mboxMessage _mboxMMDF.__init__(self, path, factory, create) - def _pre_message_hook(self, f): - """Called before writing each message to file f.""" - if f.tell() != 0: - f.write(linesep) + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(linesep) def _generate_toc(self): """Generate key-to-(start, stop) table of contents.""" starts, stops = [], [] + last_was_empty = False self._file.seek(0) while True: line_pos = self._file.tell() line = self._file.readline() if line.startswith(b'From '): if len(stops) < len(starts): - stops.append(line_pos - len(linesep)) + if last_was_empty: + stops.append(line_pos - len(linesep)) + else: + # The last line before the "From " line wasn't + # blank, but we consider it a start of a + # message anyway. + stops.append(line_pos) starts.append(line_pos) + last_was_empty = False elif not line: - stops.append(line_pos) + if last_was_empty: + stops.append(line_pos - len(linesep)) + else: + stops.append(line_pos) break + elif line == linesep: + last_was_empty = True + else: + last_was_empty = False self._toc = dict(enumerate(zip(starts, stops))) self._next_key = len(self._toc) self._file_length = self._file.tell() |