summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 59ea64a)
raw | patch | inline | side by side (parent: 59ea64a)
author | schlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Tue, 14 Jul 2009 09:10:43 +0000 (09:10 +0000) | ||
committer | schlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Tue, 14 Jul 2009 09:10:43 +0000 (09:10 +0000) |
single big transaction. Now we process each message coming in via the
mail-gateway in its own transaction. Regression-tests passed.
See also discussion:
http://thread.gmane.org/gmane.comp.bug-tracking.roundup.user/9500
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4315 57a73879-2fb5-44c3-a270-3262357dd7e2
mail-gateway in its own transaction. Regression-tests passed.
See also discussion:
http://thread.gmane.org/gmane.comp.bug-tracking.roundup.user/9500
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4315 57a73879-2fb5-44c3-a270-3262357dd7e2
diff --git a/CHANGES.txt b/CHANGES.txt
index 090aa4fdb6eba1ff3e059b5cf16c404ba30f32c4..96fc1512e81bb13a379882f18a1ee1d3fc2a3425 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
- fixed action taken in response to invalid GET request
- fixed classic tracker template to submit POST requests when appropriate
- fix problems with french and german locale files (issue 2550546)
+- Run each message of the mail-gateway in a separate transaction,
+ see http://thread.gmane.org/gmane.comp.bug-tracking.roundup.user/9500
2009-03-18 1.4.8 (r4209)
diff --git a/doc/upgrading.txt b/doc/upgrading.txt
index a2248720c61b1ed39ddf551be877582939aae4a0..b59960248f7bfec8fc1469e999cfeb3c158f09cc 100644 (file)
--- a/doc/upgrading.txt
+++ b/doc/upgrading.txt
Migrating from 1.4.x to 1.4.9
=============================
+Customized MailGW Class
+-----------------------
+
+If you have customized the MailGW class in your tracker: The new MailGW
+class opens the database for each message in the method handle_message
+(instance.open) instead of passing the opened database as a parameter to
+the MailGW constructor. The old handle_message has been renamed to
+_handle_message. The new method opens the database and wraps the call to
+the old method into a try/finally.
+
+Your customized MailGW class needs to mirror this behavior.
+
Fix the "remove" button in issue files and messages lists
---------------------------------------------------------
diff --git a/roundup/instance.py b/roundup/instance.py
index 127590b2dc8ce72dfa4c6842a51ba7def64480c7..06cff33558c25a761a5084c980de13fdd0a933ab 100644 (file)
--- a/roundup/instance.py
+++ b/roundup/instance.py
"""
self.tracker_home = tracker_home
self.optimize = optimize
+ # if set, call schema_hook after executing schema.py will get
+ # same variables (in particular db) as schema.py main purpose is
+ # for regression tests
+ self.schema_hook = None
self.config = configuration.CoreConfig(tracker_home)
self.actions = {}
self.cgi_actions = {}
if self.optimize:
# execute preloaded schema object
exec(self.schema, vars)
+ if callable (self.schema_hook):
+ self.schema_hook(**vars)
# use preloaded detectors
detectors = self.detectors
else:
sys.path.insert(1, libdir)
# execute the schema file
self._load_python('schema.py', vars)
+ if callable (self.schema_hook):
+ self.schema_hook(**vars)
# reload extensions and detectors
for extension in self.get_extensions('extensions'):
extension(self)
diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index a68d92d7659edbdda3b94fa1b3eaade17d6f27c7..683b1728b784778a36f341281f72c5b55594d434 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
class MailGW:
- def __init__(self, instance, db, arguments=()):
+ def __init__(self, instance, arguments=()):
self.instance = instance
- self.db = db
self.arguments = arguments
self.default_class = None
for option, value in self.arguments:
Parse the message as per the module docstring.
'''
+ # get database handle for handling one email
+ self.db = self.instance.open ('admin')
+ try:
+ return self._handle_message (message)
+ finally:
+ self.db.close()
+
+ def _handle_message(self, message):
+ ''' message - a Message instance
+
+ Parse the message as per the module docstring.
+
+ The implementation expects an opened database and a try/finally
+ that closes the database.
+ '''
# detect loops
if message.getheader('x-roundup-loop', ''):
raise IgnoreLoop
index b2f10b88c973bfbdf0df71296f1bd38312eb4101..3325091a4e0d4692fec5896b11af5d5b5464bc35 100644 (file)
import roundup.instance
instance = roundup.instance.open(instance_home)
- # get a mail handler
- db = instance.open('admin')
+ if hasattr(instance, 'MailGW'):
+ handler = instance.MailGW(instance, optionsList)
+ else:
+ handler = mailgw.MailGW(instance, optionsList)
+
+ # if there's no more arguments, read a single message from stdin
+ if len(args) == 1:
+ return handler.do_pipe()
+
+ # otherwise, figure what sort of mail source to handle
+ if len(args) < 3:
+ return usage(argv, _('Error: not enough source specification information'))
+ source, specification = args[1:3]
+
+ # time out net connections after a minute if we can
+ if source not in ('mailbox', 'imaps'):
+ if hasattr(socket, 'setdefaulttimeout'):
+ socket.setdefaulttimeout(60)
+
+ if source == 'mailbox':
+ return handler.do_mailbox(specification)
- # now wrap in try/finally so we always close the database
+ # the source will be a network server, so obtain the credentials to
+ # use in connecting to the server
try:
- if hasattr(instance, 'MailGW'):
- handler = instance.MailGW(instance, db, optionsList)
+ # attempt to obtain credentials from a ~/.netrc file
+ authenticator = netrc.netrc().authenticators(specification)
+ username = authenticator[0]
+ password = authenticator[2]
+ server = specification
+ # IOError if no ~/.netrc file, TypeError if the hostname
+ # not found in the ~/.netrc file:
+ except (IOError, TypeError):
+ match = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)',
+ specification)
+ if match:
+ username = match.group('user')
+ password = match.group('pass')
+ server = match.group('server')
else:
- handler = mailgw.MailGW(instance, db, optionsList)
-
- # if there's no more arguments, read a single message from stdin
- if len(args) == 1:
- return handler.do_pipe()
-
- # otherwise, figure what sort of mail source to handle
- if len(args) < 3:
- return usage(argv, _('Error: not enough source specification information'))
- source, specification = args[1:3]
-
- # time out net connections after a minute if we can
- if source not in ('mailbox', 'imaps'):
- if hasattr(socket, 'setdefaulttimeout'):
- socket.setdefaulttimeout(60)
-
- if source == 'mailbox':
- return handler.do_mailbox(specification)
-
- # the source will be a network server, so obtain the credentials to
- # use in connecting to the server
- try:
- # attempt to obtain credentials from a ~/.netrc file
- authenticator = netrc.netrc().authenticators(specification)
- username = authenticator[0]
- password = authenticator[2]
- server = specification
- # IOError if no ~/.netrc file, TypeError if the hostname
- # not found in the ~/.netrc file:
- except (IOError, TypeError):
- match = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)',
- specification)
- if match:
- username = match.group('user')
- password = match.group('pass')
- server = match.group('server')
- else:
- return usage(argv, _('Error: %s specification not valid') % source)
-
- # now invoke the mailgw handler depending on the server handler requested
- if source.startswith('pop'):
- ssl = source.endswith('s')
- if ssl and sys.version_info<(2,4):
- return usage(argv, _('Error: a later version of python is required'))
- return handler.do_pop(server, username, password, ssl)
- elif source == 'apop':
- return handler.do_apop(server, username, password)
- elif source.startswith('imap'):
- ssl = source.endswith('s')
- mailbox = ''
- if len(args) > 3:
- mailbox = args[3]
- return handler.do_imap(server, username, password, mailbox, ssl)
-
- return usage(argv, _('Error: The source must be either "mailbox",'
- ' "pop", "pops", "apop", "imap" or "imaps"'))
- finally:
- # handler might have closed the initial db and opened a new one
- handler.db.close()
+ return usage(argv, _('Error: %s specification not valid') % source)
+
+ # now invoke the mailgw handler depending on the server handler requested
+ if source.startswith('pop'):
+ ssl = source.endswith('s')
+ if ssl and sys.version_info<(2,4):
+ return usage(argv, _('Error: a later version of python is required'))
+ return handler.do_pop(server, username, password, ssl)
+ elif source == 'apop':
+ return handler.do_apop(server, username, password)
+ elif source.startswith('imap'):
+ ssl = source.endswith('s')
+ mailbox = ''
+ if len(args) > 3:
+ mailbox = args[3]
+ return handler.do_imap(server, username, password, mailbox, ssl)
+
+ return usage(argv, _('Error: The source must be either "mailbox",'
+ ' "pop", "pops", "apop", "imap" or "imaps"'))
def run():
sys.exit(main(sys.argv))
diff --git a/test/test_mailgw.py b/test/test_mailgw.py
index 7d7fba64c039b9c51d6202b552845c6cf71ac7a7..60d1164e7d3d37b2fb37b2573c0537672d191aa8 100644 (file)
--- a/test/test_mailgw.py
+++ b/test/test_mailgw.py
address='chef@bork.bork.bork', realname='Bork, Chef', roles='User')
self.richard_id = self.db.user.create(username='richard',
address='richard@test.test', roles='User')
- self.mary_id = self.db.user.create(username='mary', address='mary@test.test',
- roles='User', realname='Contrary, Mary')
- self.john_id = self.db.user.create(username='john', address='john@test.test',
- alternate_addresses='jondoe@test.test\njohn.doe@test.test', roles='User',
- realname='John Doe')
+ self.mary_id = self.db.user.create(username='mary',
+ address='mary@test.test', roles='User', realname='Contrary, Mary')
+ self.john_id = self.db.user.create(username='john',
+ address='john@test.test', roles='User', realname='John Doe',
+ alternate_addresses='jondoe@test.test\njohn.doe@test.test')
def tearDown(self):
if os.path.exists(SENDMAILDEBUG):
if error.errno not in (errno.ENOENT, errno.ESRCH): raise
def _handle_mail(self, message):
- handler = self.instance.MailGW(self.instance, self.db)
+ # handler will open a new db handle. On single-threaded
+ # databases we'll have to close our current connection
+ self.db.commit()
+ self.db.close()
+ handler = self.instance.MailGW(self.instance)
handler.trapExceptions = 0
ret = handler.main(StringIO(message))
- # handler can close the db on us and open a new one
- self.db = handler.db
+ # handler had its own database, open new connection
+ self.db = self.instance.open('admin')
return ret
def _get_mail(self):
self.doNewIssue()
oldvalues = self.db.getnode('issue', '1').copy()
oldvalues['assignedto'] = None
+ # reconstruct old behaviour: This would reuse the
+ # database-handle from the doNewIssue above which has committed
+ # as user "Chef". So we close and reopen the db as that user.
+ self.db.close()
+ self.db = self.instance.open('Chef')
self.db.issue.set('1', assignedto=self.chef_id)
self.db.commit()
self.db.issue.nosymessage('1', None, oldvalues)
assert not os.path.exists(SENDMAILDEBUG)
def testNewUserAuthor(self):
- # first without the permission
- # heh... just ignore the API for a second ;)
- self.db.security.role['anonymous'].permissions=[]
- anonid = self.db.user.lookup('anonymous')
- self.db.user.set(anonid, roles='Anonymous')
l = self.db.user.list()
l.sort()
This is a test submission of a new issue.
'''
+ def hook (db, **kw):
+ ''' set up callback for db open '''
+ db.security.role['anonymous'].permissions=[]
+ anonid = db.user.lookup('anonymous')
+ db.user.set(anonid, roles='Anonymous')
+ self.instance.schema_hook = hook
try:
self._handle_mail(message)
except Unauthorized, value:
else:
raise AssertionError, "Unathorized not raised when handling mail"
- # Add Web Access role to anonymous, and try again to make sure
- # we get a "please register at:" message this time.
- p = [
- self.db.security.getPermission('Create', 'user'),
- self.db.security.getPermission('Web Access', None),
- ]
-
- self.db.security.role['anonymous'].permissions=p
+ def hook (db, **kw):
+ ''' set up callback for db open '''
+ # Add Web Access role to anonymous, and try again to make sure
+ # we get a "please register at:" message this time.
+ p = [
+ db.security.getPermission('Create', 'user'),
+ db.security.getPermission('Web Access', None),
+ ]
+ db.security.role['anonymous'].permissions=p
+ self.instance.schema_hook = hook
try:
self._handle_mail(message)
except Unauthorized, value:
m.sort()
self.assertEqual(l, m)
- # now with the permission
- p = [
- self.db.security.getPermission('Create', 'user'),
- self.db.security.getPermission('Email Access', None),
- ]
- self.db.security.role['anonymous'].permissions=p
+ def hook (db, **kw):
+ ''' set up callback for db open '''
+ # now with the permission
+ p = [
+ db.security.getPermission('Create', 'user'),
+ db.security.getPermission('Email Access', None),
+ ]
+ db.security.role['anonymous'].permissions=p
+ self.instance.schema_hook = hook
self._handle_mail(message)
m = self.db.user.list()
m.sort()
This is a test submission of a new issue.
'''
- p = [
- self.db.security.getPermission('Create', 'user'),
- self.db.security.getPermission('Email Access', None),
- ]
- self.db.security.role['anonymous'].permissions=p
+ def hook (db, **kw):
+ ''' set up callback for db open '''
+ p = [
+ db.security.getPermission('Create', 'user'),
+ db.security.getPermission('Email Access', None),
+ ]
+ db.security.role['anonymous'].permissions=p
+ self.instance.schema_hook = hook
self._handle_mail(message)
m = set(self.db.user.list())
new = list(m - l)[0]