diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index ed135bf02cb968..3b0c17c838c879 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -78,6 +78,14 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. message. Failing to lock the mailbox runs the risk of losing messages or corrupting the entire mailbox. + The :class:`!Mailbox` class supports the :keyword:`with` statement. When used + as a context manager, :class:`!Mailbox` calls :meth:`lock` when the context is entered, + returns the mailbox object as the context object, and at context end calls :meth:`close`, + thereby releasing the lock. + + .. versionchanged:: next + Support for the :keyword:`with` statement was added. + :class:`!Mailbox` instances have the following methods: diff --git a/Lib/mailbox.py b/Lib/mailbox.py index 65923e9c5de324..99426220154360 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -39,6 +39,13 @@ def __init__(self, path, factory=None, create=True): self._path = os.path.abspath(os.path.expanduser(path)) self._factory = factory + def __enter__(self): + self.lock() + return self + + def __exit__(self, type, value, traceback): + self.close() + def add(self, message): """Add message and return assigned key.""" raise NotImplementedError('Method must be implemented by subclass') diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 288b2c4496faa1..7421076ddd4c3a 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -542,6 +542,11 @@ def _test_flush_or_close(self, method, should_call_close): self.assertIn(self._box.get_string(key), contents) oldbox.close() + def test_use_context_manager(self): + # Mailboxes are usable as a context manager + with self._box as box: + self.assertIs(self._box, box) + def test_dump_message(self): # Write message representations to disk for input in (email.message_from_string(_sample_message), @@ -1122,6 +1127,16 @@ def test_ownership_after_flush(self): self.assertEqual(st.st_gid, other_gid) self.assertEqual(st.st_mode, mode) + def test_context_manager_locks_and_closes(self): + # Context manager locks/unlocks and closes. + # (This test uses an implementation detail to get the state.) + self.assertFalse(self._box._locked) + with self._box as context_object: + self.assertIs(self._box, context_object) + self.assertTrue(self._box._locked) + self.assertFalse(self._box._file.closed) + self.assertFalse(self._box._locked) + self.assertTrue(self._box._file.closed) class _TestMboxMMDF(_TestSingleFile): diff --git a/Misc/NEWS.d/next/Library/2017-12-15-09-32-57.bpo-32234.XaOkhR.rst b/Misc/NEWS.d/next/Library/2017-12-15-09-32-57.bpo-32234.XaOkhR.rst new file mode 100644 index 00000000000000..b22289835620df --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-12-15-09-32-57.bpo-32234.XaOkhR.rst @@ -0,0 +1,2 @@ +:class:`mailbox.Mailbox` instances can now be used as a context manager. +The Mailbox is locked on context entry and unlocked and closed at context exit.