aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolaus Schulz <microschulz@web.de>2009-12-29 19:43:23 +0100
committerNikolaus Schulz <microschulz@web.de>2010-07-19 01:40:24 +0200
commit86471d12a4959f8ab760fc0dac4da266c6b6e80b (patch)
tree0fde314d3758d95df4e051f99ae0a150a488468d
parentda595427ff01e3dedccef22db839895150ed1270 (diff)
downloadarchivemail-86471d12a4959f8ab760fc0dac4da266c6b6e80b.tar.gz
archivemail-86471d12a4959f8ab760fc0dac4da266c6b6e80b.tar.bz2
archivemail-86471d12a4959f8ab760fc0dac4da266c6b6e80b.zip
Further refactoring of the mbox classes, adding archive locking support
The mbox locking methods move into a new class LockableMboxMixin, and the Mbox and ArchiveMbox classes become subclasses of LockableMboxMixin. class StaleFiles is updated to handle multiple dotlock files.
-rwxr-xr-xarchivemail.py104
1 files changed, 58 insertions, 46 deletions
diff --git a/archivemail.py b/archivemail.py
index bdc26f5..e4e8425 100755
--- a/archivemail.py
+++ b/archivemail.py
@@ -138,17 +138,17 @@ class Stats:
class StaleFiles:
"""Class to keep track of files to be deleted on abnormal exit"""
- dotlock_lock = None # original_mailbox.lock
+ dotlock_files = [] # dotlock files for source mbox and final archive
temp_mboxes = [] # temporary retain and archive mboxes
temp_dir = None # our tempfile directory container
def clean(self):
"""Delete any temporary files or lockfiles that exist"""
- if self.dotlock_lock:
- vprint("removing stale dotlock file '%s'" % self.dotlock_lock)
+ while self.dotlock_files:
+ dotlock = self.dotlock_files.pop()
+ vprint("removing stale dotlock file '%s'" % dotlock)
try:
- os.remove(self.dotlock_lock)
- self.dotlock_lock = None
+ os.remove(dotlock)
except (IOError, OSError): pass
while self.temp_mboxes:
mbox = self.temp_mboxes.pop()
@@ -311,40 +311,13 @@ class Options:
string)
-class Mbox(mailbox.UnixMailbox):
- """A mostly-read-only mbox with locking. The mbox content can only be
- modified by overwriting the entire underlying file."""
+class LockableMboxMixin:
+ """Locking methods for mbox files."""
- def __init__(self, path):
- """Constructor for opening an existing 'mbox' mailbox.
- Extends constructor for mailbox.UnixMailbox()
-
- Named Arguments:
- path -- file name of the 'mbox' file to be opened
- """
- assert(path)
+ def __init__(self, mbox_file, mbox_file_name):
+ self.mbox_file = mbox_file
+ self.mbox_file_name = mbox_file_name
self._locked = False
- fd = safe_open_existing(path)
- st = os.fstat(fd)
- self.original_atime = st.st_atime
- self.original_mtime = st.st_mtime
- self.starting_size = st.st_size
- self.mbox_file = os.fdopen(fd, "r+")
- self.mbox_file_name = path
- mailbox.UnixMailbox.__init__(self, self.mbox_file)
-
- def close(self):
- """Close the mbox file"""
- vprint("closing file '%s'" % self.mbox_file_name)
- self.mbox_file.close()
-
- def reset_timestamps(self):
- """Set the file timestamps to the original value"""
- assert(self.original_atime)
- assert(self.original_mtime)
- assert(self.mbox_file_name)
- os.utime(self.mbox_file_name, (self.original_atime, \
- self.original_mtime))
def lock(self):
"""Lock this mbox with both a dotlock and a posix lock."""
@@ -427,8 +400,8 @@ class Mbox(mailbox.UnixMailbox):
finally:
os.close(plfd)
os.unlink(prelock_name)
- _stale.dotlock_lock = lock_name
vprint("acquired lockfile '%s'" % lock_name)
+ _stale.dotlock_files.append(lock_name)
def _dotlock_unlock(self):
"""Delete the dotlock file for the 'mbox' mailbox."""
@@ -436,7 +409,44 @@ class Mbox(mailbox.UnixMailbox):
lock_name = self.mbox_file_name + options.lockfile_extension
vprint("removing lockfile '%s'" % lock_name)
os.remove(lock_name)
- _stale.dotlock_lock = None
+ _stale.dotlock_files.remove(lock_name)
+
+ def close(self):
+ """Close the mbox file"""
+ vprint("closing file '%s'" % self.mbox_file_name)
+ assert(not self._locked)
+ self.mbox_file.close()
+
+
+class Mbox(mailbox.UnixMailbox, LockableMboxMixin):
+ """A mostly-read-only mbox with locking. The mbox content can only be
+ modified by overwriting the entire underlying file."""
+
+ def __init__(self, path):
+ """Constructor for opening an existing 'mbox' mailbox.
+ Extends constructor for mailbox.UnixMailbox()
+
+ Named Arguments:
+ path -- file name of the 'mbox' file to be opened
+ """
+ assert(path)
+ fd = safe_open_existing(path)
+ st = os.fstat(fd)
+ self.original_atime = st.st_atime
+ self.original_mtime = st.st_mtime
+ self.starting_size = st.st_size
+ self.mbox_file = os.fdopen(fd, "r+")
+ self.mbox_file_name = path
+ LockableMboxMixin.__init__(self, self.mbox_file, path)
+ mailbox.UnixMailbox.__init__(self, self.mbox_file)
+
+ def reset_timestamps(self):
+ """Set the file timestamps to the original values"""
+ assert(self.original_atime)
+ assert(self.original_mtime)
+ assert(self.mbox_file_name)
+ os.utime(self.mbox_file_name, (self.original_atime, \
+ self.original_mtime))
def get_size(self):
"""Return the current size of the mbox file"""
@@ -450,15 +460,17 @@ class Mbox(mailbox.UnixMailbox):
self.mbox_file.truncate()
-class ArchiveMbox:
+class ArchiveMbox(LockableMboxMixin):
"""Simple append-only access to the archive mbox. Entirely content-agnostic."""
def __init__(self, path):
fd = safe_open(path)
self.mbox_file = os.fdopen(fd, "a")
+ LockableMboxMixin.__init__(self, self.mbox_file, path)
def append(self, filename):
"""Append the content of the given file to the mbox."""
+ assert(self._locked)
fin = open(filename, "r")
oldsize = os.fstat(self.mbox_file.fileno()).st_size
try:
@@ -470,10 +482,6 @@ class ArchiveMbox:
raise
fin.close()
- def close(self):
- """Close the mbox file."""
- self.mbox_file.close()
-
class TempMbox:
"""An write-only temporary mbox. No locking methods."""
@@ -1566,8 +1574,12 @@ def commit_archive(archive, final_archive_name):
archive.close()
if not archive.empty:
final_archive = ArchiveMbox(final_archive_name)
- final_archive.append(archive.mbox_file_name)
- final_archive.close()
+ final_archive.lock()
+ try:
+ final_archive.append(archive.mbox_file_name)
+ finally:
+ final_archive.unlock()
+ final_archive.close()
archive.remove()
def make_archive_name(mailbox_name):