123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- """http://code.activestate.com/recipes/413838-memento-closure/"""
- import copy
- def Memento(obj, deep=False):
- state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__)
- def Restore():
- obj.__dict__.clear()
- obj.__dict__.update(state)
- return Restore
- class Transaction:
- """A transaction guard. This is really just
- syntactic suggar arount a memento closure.
- """
- deep = False
- def __init__(self, *targets):
- self.targets = targets
- self.Commit()
- def Commit(self):
- self.states = [Memento(target, self.deep) for target in self.targets]
- def Rollback(self):
- for st in self.states:
- st()
- class transactional(object):
- """Adds transactional semantics to methods. Methods decorated with
- @transactional will rollback to entry state upon exceptions.
- """
- def __init__(self, method):
- self.method = method
- def __get__(self, obj, T):
- def transaction(*args, **kwargs):
- state = Memento(obj)
- try:
- return self.method(obj, *args, **kwargs)
- except:
- state()
- raise
- return transaction
- class NumObj(object):
- def __init__(self, value):
- self.value = value
- def __repr__(self):
- return '<%s: %r>' % (self.__class__.__name__, self.value)
- def Increment(self):
- self.value += 1
- @transactional
- def DoStuff(self):
- self.value = '1111' # <- invalid value
- self.Increment() # <- will fail and rollback
- if __name__ == '__main__':
- n = NumObj(-1)
- print(n)
- t = Transaction(n)
- try:
- for i in range(3):
- n.Increment()
- print(n)
- t.Commit()
- print('-- commited')
- for i in range(3):
- n.Increment()
- print(n)
- n.value += 'x' # will fail
- print(n)
- except:
- t.Rollback()
- print('-- rolled back')
- print(n)
- print('-- now doing stuff ...')
- try:
- n.DoStuff()
- except:
- print('-> doing stuff failed!')
- import sys
- import traceback
- traceback.print_exc(file=sys.stdout)
- pass
- print(n)
- ### OUTPUT ###
- # <NumObj: -1>
- # <NumObj: 0>
- # <NumObj: 1>
- # <NumObj: 2>
- # -- commited
- # <NumObj: 3>
- # <NumObj: 4>
- # <NumObj: 5>
- # -- rolled back
- # <NumObj: 2>
- # -- now doing stuff ...
- # -> doing stuff failed!
- # Traceback (most recent call last):
- # File "memento.py", line 91, in <module>
- # n.DoStuff()
- # File "memento.py", line 47, in transaction
- # return self.method(obj, *args, **kwargs)
- # File "memento.py", line 67, in DoStuff
- # self.Increment() # <- will fail and rollback
- # File "memento.py", line 62, in Increment
- # self.value += 1
- # TypeError: Can't convert 'int' object to str implicitly
- # <NumObj: 2>
|