On Sat, 2012-03-31 at 07:49 -0400, Jim Fulton wrote: > 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.
Alright, I took a stab at it with tests, albeit as a method of the TransactionManager class (and an alias in __init__ as "job"): https://mail.zope.org/pipermail/checkins/2012-April/059120.html - C _______________________________________________ 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