|
@@ -1,91 +1,94 @@
|
|
|
-'''code.activestate.com/recipes/413838-memento-closure/'''
|
|
|
+"""code.activestate.com/recipes/413838-memento-closure/"""
|
|
|
|
|
|
import copy
|
|
|
|
|
|
+
|
|
|
def Memento(obj, deep=False):
|
|
|
- state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__)
|
|
|
+ state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__)
|
|
|
+
|
|
|
+ def Restore():
|
|
|
+ obj.__dict__.clear()
|
|
|
+ obj.__dict__.update(state)
|
|
|
+ return Restore
|
|
|
|
|
|
- def Restore():
|
|
|
- obj.__dict__.clear()
|
|
|
- obj.__dict__.update(state)
|
|
|
- return Restore
|
|
|
|
|
|
class Transaction:
|
|
|
- """A transaction guard. This is really just
|
|
|
+ """A transaction guard. This is really just
|
|
|
syntactic suggar arount a memento closure.
|
|
|
- """
|
|
|
- deep = False
|
|
|
+ """
|
|
|
+ deep = False
|
|
|
+
|
|
|
+ def __init__(self, *targets):
|
|
|
+ self.targets = targets
|
|
|
+ self.Commit()
|
|
|
|
|
|
- def __init__(self, *targets):
|
|
|
- self.targets = targets
|
|
|
- self.Commit()
|
|
|
+ def Commit(self):
|
|
|
+ self.states = [Memento(target, self.deep) for target in self.targets]
|
|
|
|
|
|
- def Commit(self):
|
|
|
- self.states = [Memento(target, self.deep) for target in self.targets]
|
|
|
+ def Rollback(self):
|
|
|
+ for st in self.states:
|
|
|
+ st()
|
|
|
|
|
|
- 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
|
|
|
+ """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 __init__(self, value):
|
|
|
+ self.value = value
|
|
|
|
|
|
- def __repr__(self):
|
|
|
- return '<%s: %r>' % (self.__class__.__name__, self.value)
|
|
|
+ def __repr__(self):
|
|
|
+ return '<%s: %r>' % (self.__class__.__name__, self.value)
|
|
|
|
|
|
- def Increment(self):
|
|
|
- self.value += 1
|
|
|
+ def Increment(self):
|
|
|
+ self.value += 1
|
|
|
|
|
|
- @transactional
|
|
|
- def DoStuff(self):
|
|
|
- self.value = '1111' # <- invalid value
|
|
|
- self.Increment() # <- will fail and rollback
|
|
|
+ @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 traceback
|
|
|
- traceback.print_exc(0)
|
|
|
- pass
|
|
|
- print(n)
|
|
|
+ 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 traceback
|
|
|
+ traceback.print_exc(0)
|
|
|
+ pass
|
|
|
+ print(n)
|