On Thu, Mar 29, 2012 at 6:59 PM, Chris McDonough <chr...@plope.com> wrote: > On Thu, 2012-03-29 at 07:18 -0400, Jim Fulton wrote: >> On Wed, Mar 28, 2012 at 6:37 PM, Chris McDonough <chr...@plope.com> wrote: >> > On Wed, 2012-03-28 at 17:06 -0400, Jim Fulton wrote: >> >> On Wed, Mar 28, 2012 at 4:37 PM, Chris McDonough <chr...@plope.com> wrote: >> >> > On Wed, 2012-03-28 at 14:21 -0400, Jim Fulton wrote: >> >> ... >> >> > A decorator for running some code in the context of a txn and retrying >> >> > retryable exceptions would be nice higher level behavior. Â I'd be >> >> > willing to do this work over this weekend. >> >> >> >> Cool. Don't forget the transaction note part. :) >> >> Too many transactions in our apps don't have >> >> notes, especially transactions that happen >> >> outside of web requests. >> >> >> >> > In the meantime, I think the existing attempts context manager still >> >> > needs the small fix I proposed in my original message. Â Can you confirm >> >> > that my understanding of the its intent seems roughly correct? >> >> >> >> I didn't see a statement of intent. I think your fix us good, >> >> but the code (that I wrote and your fix) makes my head hurt. :) >> >> >> >> I'd say, make some test cases for the bug and make it pass. >> > >> > OK. Once I fix this "Attempts" bug, I think the decorator code is just >> > a higher level interface that uses it: >> > >> > class job(object): >> > def __init__(self, attempts=1, note=None, manager=None): >> > self.attempts = attempts >> > self.note = note >> > if manager is None: >> > manager = transaction.manager >> > self.manager = manager >> > >> > def __call__(self, wrapped): >> > note = self.note >> > if note is None: >> > note = getattr(wrapped, '__name__', None) >> > def inner(*arg, **kw): >> > for attempt in self.manager.attempts(self.attempts): >> > with attempt as t: >> > t.note(note) >> > return wrapped(*arg, **kw) >> > >> > .. or something like that... >> >> It could be written that way, although I would >> use a much simpler implementation. >> >> The attempt design was a reach to overcome >> the limitations of the with statement. I'm not at all happy >> with it, although I couldn't think of anything better at the >> time. I hate to build on it. > > It's brainbusting, yes, but it works. Do you have any other specific > pattern in mind?
def job(func=None, retries=3): if func is None: return lambda f: job(f, retries) note = func.__doc__ if note: note = note.split('\n', 1)[0] else: note = func.__name__ for i in xrange(retries + 1): t = manager.begin() if i: t.note("%s (retry: %s)" % (note, i)) else: t.note(note) try: func(t) t.commit() except TransientError: t.abort() else: break The above is untested, but you get the idea. Aside from note and decorator support, this is straightforward. It encapsulates standard boilerplate. Jim -- Jim Fulton http://www.linkedin.com/in/jimfulton _______________________________________________ For more information about ZODB, see http://zodb.org/ ZODB-Dev mailing list - ZODB-Dev@zope.org https://mail.zope.org/mailman/listinfo/zodb-dev