33
44"""http://code.activestate.com/recipes/413838-memento-closure/"""
55
6- import copy
6+ from copy import copy , deepcopy
77
88
9- def Memento (obj , deep = False ):
10- state = ( copy . copy , copy . deepcopy )[ bool ( deep )] (obj .__dict__ )
9+ def memento (obj , deep = False ):
10+ state = copy ( obj . __dict__ ) if deep else deepcopy (obj .__dict__ )
1111
12- def Restore ():
12+ def restore ():
1313 obj .__dict__ .clear ()
1414 obj .__dict__ .update (state )
15- return Restore
1615
16+ return restore
1717
18- class Transaction :
1918
20- """A transaction guard. This is really just
21- syntactic suggar arount a memento closure.
22- """
19+ class Transaction :
20+ """A transaction guard.
21+ This is, in fact, just syntactic sugar around a memento closure.
22+ """
2323 deep = False
24+ states = []
2425
2526 def __init__ (self , * targets ):
2627 self .targets = targets
27- self .Commit ()
28+ self .commit ()
2829
29- def Commit (self ):
30- self .states = [Memento (target , self .deep ) for target in self .targets ]
30+ def commit (self ):
31+ self .states = [memento (target , self .deep ) for target in self .targets ]
3132
32- def Rollback (self ):
33- for st in self .states :
34- st ()
33+ def rollback (self ):
34+ for a_state in self .states :
35+ a_state ()
3536
3637
37- class transactional (object ):
38-
38+ class Transactional (object ):
3939 """Adds transactional semantics to methods. Methods decorated with
40- @transactional will rollback to entry state upon exceptions.
40+ @Transactional will rollback to entry- state upon exceptions.
4141 """
4242
4343 def __init__ (self , method ):
4444 self .method = method
4545
4646 def __get__ (self , obj , T ):
4747 def transaction (* args , ** kwargs ):
48- state = Memento (obj )
48+ state = memento (obj )
4949 try :
5050 return self .method (obj , * args , ** kwargs )
51- except :
51+ except Exception as e :
5252 state ()
53- raise
53+ raise e
54+
5455 return transaction
5556
5657
5758class NumObj (object ):
58-
5959 def __init__ (self , value ):
6060 self .value = value
6161
6262 def __repr__ (self ):
6363 return '<%s: %r>' % (self .__class__ .__name__ , self .value )
6464
65- def Increment (self ):
65+ def increment (self ):
6666 self .value += 1
6767
68- @transactional
69- def DoStuff (self ):
68+ @Transactional
69+ def do_stuff (self ):
7070 self .value = '1111' # <- invalid value
71- self .Increment () # <- will fail and rollback
71+ self .increment () # <- will fail and rollback
7272
7373
7474if __name__ == '__main__' :
75- n = NumObj (- 1 )
76- print (n )
77- t = Transaction (n )
75+ num_obj = NumObj (- 1 )
76+ print (num_obj )
77+
78+ a_transaction = Transaction (num_obj )
7879 try :
7980 for i in range (3 ):
80- n .Increment ()
81- print (n )
82- t .Commit ()
83- print ('-- commited' )
81+ num_obj .increment ()
82+ print (num_obj )
83+ a_transaction .commit ()
84+ print ('-- committed' )
85+
8486 for i in range (3 ):
85- n . Increment ()
86- print (n )
87- n .value += 'x' # will fail
88- print (n )
89- except :
90- t . Rollback ()
87+ num_obj . increment ()
88+ print (num_obj )
89+ num_obj .value += 'x' # will fail
90+ print (num_obj )
91+ except Exception as e :
92+ a_transaction . rollback ()
9193 print ('-- rolled back' )
92- print (n )
94+ print (num_obj )
95+
9396 print ('-- now doing stuff ...' )
9497 try :
95- n . DoStuff ()
96- except :
98+ num_obj . do_stuff ()
99+ except Exception as e :
97100 print ('-> doing stuff failed!' )
98101 import sys
99102 import traceback
103+
100104 traceback .print_exc (file = sys .stdout )
101- pass
102- print ( n )
105+ print ( num_obj )
106+
103107
104108### OUTPUT ###
105109# <NumObj: -1>
106110# <NumObj: 0>
107111# <NumObj: 1>
108112# <NumObj: 2>
109- # -- commited
113+ # -- committed
110114# <NumObj: 3>
111115# <NumObj: 4>
112116# <NumObj: 5>
@@ -115,13 +119,15 @@ def DoStuff(self):
115119# -- now doing stuff ...
116120# -> doing stuff failed!
117121# Traceback (most recent call last):
118- # File "memento.py", line 91, in <module>
119- # n.DoStuff()
120- # File "memento.py", line 47, in transaction
122+ # File "memento.py", line 97, in <module>
123+ # num_obj.do_stuff()
124+ # File "memento.py", line 52, in transaction
125+ # raise e
126+ # File "memento.py", line 49, in transaction
121127# return self.method(obj, *args, **kwargs)
122- # File "memento.py", line 67 , in DoStuff
123- # self.Increment () # <- will fail and rollback
124- # File "memento.py", line 62 , in Increment
128+ # File "memento.py", line 70 , in do_stuff
129+ # self.increment () # <- will fail and rollback
130+ # File "memento.py", line 65 , in increment
125131# self.value += 1
126132# TypeError: Can't convert 'int' object to str implicitly
127133# <NumObj: 2>
0 commit comments