[Checkins] SVN: zope.sendmail/trunk/src/zope/sendmail/ Don't keep
open files around for every email message to be sent on transaction
Marius Gedminas
marius at pov.lt
Thu Jun 7 09:24:21 EDT 2007
Log message for revision 76463:
Don't keep open files around for every email message to be sent on transaction
commit. People who try to send many emails in a single transaction now will
not run out of file descriptors.
Changed:
U zope.sendmail/trunk/src/zope/sendmail/delivery.py
U zope.sendmail/trunk/src/zope/sendmail/interfaces.py
U zope.sendmail/trunk/src/zope/sendmail/maildir.py
U zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py
U zope.sendmail/trunk/src/zope/sendmail/tests/test_maildir.py
-=-
Modified: zope.sendmail/trunk/src/zope/sendmail/delivery.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/delivery.py 2007-06-07 12:55:05 UTC (rev 76462)
+++ zope.sendmail/trunk/src/zope/sendmail/delivery.py 2007-06-07 13:24:21 UTC (rev 76463)
@@ -134,6 +134,7 @@
msg.write('X-Zope-From: %s\n' % fromaddr)
msg.write('X-Zope-To: %s\n' % ", ".join(toaddrs))
msg.write(message)
+ msg.close()
return MailDataManager(msg.commit, onAbort=msg.abort)
Modified: zope.sendmail/trunk/src/zope/sendmail/interfaces.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/interfaces.py 2007-06-07 12:55:05 UTC (rev 76462)
+++ zope.sendmail/trunk/src/zope/sendmail/interfaces.py 2007-06-07 13:24:21 UTC (rev 76463)
@@ -243,6 +243,15 @@
any line separators.
"""
+ def close():
+ """Closes the message file.
+
+ No further writes are allowed. You can call ``close()`` before calling
+ ``commit()`` or ``abort()`` to avoid having too many open files.
+
+ Calling ``close()`` more than once is allowed.
+ """
+
def commit():
"""Commits the new message using the `Maildir` protocol.
Modified: zope.sendmail/trunk/src/zope/sendmail/maildir.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/maildir.py 2007-06-07 12:55:05 UTC (rev 76462)
+++ zope.sendmail/trunk/src/zope/sendmail/maildir.py 2007-06-07 13:24:21 UTC (rev 76463)
@@ -18,6 +18,7 @@
__docformat__ = 'restructuredtext'
import os
+import errno
import socket
import time
import random
@@ -91,7 +92,9 @@
filename = join(subdir_tmp, unique)
try:
fd = os.open(filename, os.O_CREAT|os.O_EXCL|os.O_WRONLY, 0600)
- except OSError:
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
# File exists
counter += 1
if counter >= 1000:
@@ -115,7 +118,7 @@
self._filename = filename
self._new_filename = new_filename
self._fd = fd
- self._closed = False
+ self._finished = False
self._aborted = False
def write(self, data):
@@ -124,22 +127,28 @@
def writelines(self, lines):
self._fd.writelines(lines)
+ def close(self):
+ self._fd.close()
+
def commit(self):
- if self._closed and self._aborted:
+ if self._aborted:
raise RuntimeError('Cannot commit, message already aborted')
- elif not self._closed:
- self._closed = True
- self._aborted = False
+ elif not self._finished:
+ self._finished = True
self._fd.close()
os.rename(self._filename, self._new_filename)
# NOTE: the same maildir.html says it should be a link, followed by
# unlink. But Win32 does not necessarily have hardlinks!
def abort(self):
- if not self._closed:
- self._closed = True
+ # XXX mgedmin: I think it is dangerous to have an abort() that does
+ # nothing when commit() already succeeded. But the tests currently
+ # test that expectation.
+ if not self._finished:
+ self._finished = True
self._aborted = True
self._fd.close()
os.unlink(self._filename)
- # should there be a __del__ that does abort()?
+ # XXX: should there be a __del__ that calls abort()?
+
Modified: zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py 2007-06-07 12:55:05 UTC (rev 76462)
+++ zope.sendmail/trunk/src/zope/sendmail/tests/test_delivery.py 2007-06-07 13:24:21 UTC (rev 76463)
@@ -147,18 +147,32 @@
data = ''
commited_messages = [] # this list is shared among all instances
aborted_messages = [] # this one too
+ _closed = False
def write(self, str):
+ if self._closed:
+ raise AssertionError('already closed')
self.data += str
def writelines(self, seq):
+ if self._closed:
+ raise AssertionError('already closed')
self.data += ''.join(seq)
+ def close(self):
+ self._closed = True
+
def commit(self):
+ if not self._closed:
+ raise AssertionError('for this test we want the message explicitly'
+ ' closed before it is committed')
self._commited = True
self.commited_messages.append(self.data)
def abort(self):
+ if not self._closed:
+ raise AssertionError('for this test we want the message explicitly'
+ ' closed before it is committed')
self._aborted = True
self.aborted_messages.append(self.data)
Modified: zope.sendmail/trunk/src/zope/sendmail/tests/test_maildir.py
===================================================================
--- zope.sendmail/trunk/src/zope/sendmail/tests/test_maildir.py 2007-06-07 12:55:05 UTC (rev 76462)
+++ zope.sendmail/trunk/src/zope/sendmail/tests/test_maildir.py 2007-06-07 13:24:21 UTC (rev 76463)
@@ -19,6 +19,7 @@
import unittest
import stat
import os
+import errno
from zope.interface.verify import verifyObject
@@ -123,7 +124,7 @@
def open(self, filename, flags, mode=0777):
if (flags & os.O_EXCL and flags & os.O_CREAT
and self.access(filename, 0)):
- raise OSError('file already exists')
+ raise OSError(errno.EEXIST, 'file already exists')
if not flags & os.O_CREAT and not self.access(filename, 0):
raise OSError('file not found')
fd = max(self._descriptors.keys() + [2]) + 1
More information about the Checkins
mailing list