bpo-32234: Allow mailbox instances as context managers (GH-4770)
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: R. David Murray <rdmurray@bitdance.com>
diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst
index ed135bf..3b0c17c 100644
--- a/Doc/library/mailbox.rst
+++ b/Doc/library/mailbox.rst
@@ -78,6 +78,14 @@
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 65923e9..9942622 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 288b2c4..7421076 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 0000000..b222898
--- /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.