memento.py 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. '''code.activestate.com/recipes/413838-memento-closure/'''
  2. import copy
  3. def Memento(obj, deep=False):
  4. state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__)
  5. def Restore():
  6. obj.__dict__.clear()
  7. obj.__dict__.update(state)
  8. return Restore
  9. class Transaction:
  10. """A transaction guard. This is really just
  11. syntactic suggar arount a memento closure.
  12. """
  13. deep = False
  14. def __init__(self, *targets):
  15. self.targets = targets
  16. self.Commit()
  17. def Commit(self):
  18. self.states = [Memento(target, self.deep) for target in self.targets]
  19. def Rollback(self):
  20. for st in self.states:
  21. st()
  22. class transactional(object):
  23. """Adds transactional semantics to methods. Methods decorated
  24. with @transactional will rollback to entry state upon exceptions.
  25. """
  26. def __init__(self, method):
  27. self.method = method
  28. def __get__(self, obj, T):
  29. def transaction(*args, **kwargs):
  30. state = Memento(obj)
  31. try:
  32. return self.method(obj, *args, **kwargs)
  33. except:
  34. state()
  35. raise
  36. return transaction
  37. class NumObj(object):
  38. def __init__(self, value):
  39. self.value = value
  40. def __repr__(self):
  41. return '<%s: %r>' % (self.__class__.__name__, self.value)
  42. def Increment(self):
  43. self.value += 1
  44. @transactional
  45. def DoStuff(self):
  46. self.value = '1111' # <- invalid value
  47. self.Increment() # <- will fail and rollback
  48. if __name__ == '__main__':
  49. n = NumObj(-1)
  50. print(n)
  51. t = Transaction(n)
  52. try:
  53. for i in range(3):
  54. n.Increment()
  55. print(n)
  56. t.Commit()
  57. print('-- commited')
  58. for i in range(3):
  59. n.Increment()
  60. print(n)
  61. n.value += 'x' # will fail
  62. print(n)
  63. except:
  64. t.Rollback()
  65. print('-- rolled back')
  66. print(n)
  67. print('-- now doing stuff ...')
  68. try:
  69. n.DoStuff()
  70. except:
  71. print('-> doing stuff failed!')
  72. import traceback
  73. traceback.print_exc(0)
  74. pass
  75. print(n)