From fa8153401aad10baedc549a1f56a3c7c75e3cbc4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 3 May 2009 02:52:20 +0000 Subject: [PATCH] Merged revisions 72213 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72213 | andrew.kuchling | 2009-05-02 15:17:28 -0400 (Sat, 02 May 2009) | 3 lines #1607951: Make mailbox.Maildir re-read the directories less frequently. This is done by recording the current time -1sec, and not re-reading unless the directory mod. times are >= the recorded time. ........ --- Lib/mailbox.py | 28 ++++++++++++++++++++++++---- Lib/test/test_mailbox.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/Lib/mailbox.py b/Lib/mailbox.py index 909285bd98b..85e3ab1f8d0 100755 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -233,6 +233,7 @@ class Maildir(Mailbox): else: raise NoSuchMailboxError(self._path) self._toc = {} + self._last_read = None # Records last time we read cur/new def add(self, message): """Add message and return assigned key.""" @@ -457,16 +458,35 @@ class Maildir(Mailbox): def _refresh(self): """Update table of contents mapping.""" + new_mtime = os.path.getmtime(os.path.join(self._path, 'new')) + cur_mtime = os.path.getmtime(os.path.join(self._path, 'cur')) + + if (self._last_read is not None and + new_mtime <= self._last_read and cur_mtime <= self._last_read): + return + self._toc = {} - for subdir in ('new', 'cur'): - subdir_path = os.path.join(self._path, subdir) - for entry in os.listdir(subdir_path): - p = os.path.join(subdir_path, entry) + def update_dir (subdir): + path = os.path.join(self._path, subdir) + for entry in os.listdir(path): + p = os.path.join(path, entry) if os.path.isdir(p): continue uniq = entry.split(self.colon)[0] self._toc[uniq] = os.path.join(subdir, entry) + update_dir('new') + update_dir('cur') + + # We record the current time - 1sec so that, if _refresh() is called + # again in the same second, we will always re-read the mailbox + # just in case it's been modified. (os.path.mtime() only has + # 1sec resolution.) This results in a few unnecessary re-reads + # when _refresh() is called multiple times in the same second, + # but once the clock ticks over, we will only re-read as needed. + now = int(time.time() - 1) + self._last_read = time.time() - 1 + def _lookup(self, key): """Use TOC to return subpath for given key, or raise a KeyError.""" try: diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 28b93221691..655b6391154 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -740,6 +740,37 @@ class TestMaildir(TestMailbox): perms = st.st_mode self.assertFalse((perms & 0o111)) # Execute bits should all be off. + def test_reread(self): + # Wait for 2 seconds + time.sleep(2) + + # Initially, the mailbox has not been read and the time is null. + assert getattr(self._box, '_last_read', None) is None + + # Refresh mailbox; the times should now be set to something. + self._box._refresh() + assert getattr(self._box, '_last_read', None) is not None + + # Try calling _refresh() again; the modification times shouldn't have + # changed, so the mailbox should not be re-reading. Re-reading causes + # the ._toc attribute to be assigned a new dictionary object, so + # we'll check that the ._toc attribute isn't a different object. + orig_toc = self._box._toc + def refreshed(): + return self._box._toc is not orig_toc + + time.sleep(1) # Wait 1sec to ensure time.time()'s value changes + self._box._refresh() + assert not refreshed() + + # Now, write something into cur and remove it. This changes + # the mtime and should cause a re-read. + filename = os.path.join(self._path, 'cur', 'stray-file') + f = open(filename, 'w') + f.close() + os.unlink(filename) + self._box._refresh() + assert refreshed() class _TestMboxMMDF(TestMailbox):