@@ -32,9 +32,8 @@ class Transaction(Batch):
32
32
33
33
>>> datastore.set_defaults()
34
34
35
- >>> with Transaction() as xact:
36
- ... datastore.put(entity1)
37
- ... datastore.put(entity2)
35
+ >>> with Transaction():
36
+ ... datastore.put([entity1, entity2])
38
37
39
38
Because it derives from :class:`Batch`, :class`Transaction` also provides
40
39
:meth:`put` and :meth:`delete` methods::
@@ -46,7 +45,7 @@ class Transaction(Batch):
46
45
By default, the transaction is rolled back if the transaction block
47
46
exits with an error::
48
47
49
- >>> with Transaction() as txn :
48
+ >>> with Transaction():
50
49
... do_some_work()
51
50
... raise SomeException() # rolls back
52
51
@@ -71,16 +70,34 @@ class Transaction(Batch):
71
70
... entity = Entity(key=Key('Thing'))
72
71
... datastore.put([entity])
73
72
... assert entity.key.is_partial # There is no ID on this key.
73
+ ...
74
74
>>> assert not entity.key.is_partial # There *is* an ID.
75
75
76
+ After completion, you can determine if a commit succeeded or failed.
77
+ For example, trying to delete a key that doesn't exist::
78
+
79
+ >>> with Transaction() as xact:
80
+ ... xact.delete(key)
81
+ ...
82
+ >>> xact.succeeded
83
+ False
84
+
85
+ or successfully storing two entities:
86
+
87
+ >>> with Transaction() as xact:
88
+ ... datastore.put([entity1, entity2])
89
+ ...
90
+ >>> xact.succeeded
91
+ True
92
+
76
93
If you don't want to use the context manager you can initialize a
77
94
transaction manually::
78
95
79
96
>>> transaction = Transaction()
80
97
>>> transaction.begin()
81
98
82
99
>>> entity = Entity(key=Key('Thing'))
83
- >>> transaction.put([ entity] )
100
+ >>> transaction.put(entity)
84
101
85
102
>>> if error:
86
103
... transaction.rollback()
@@ -97,9 +114,22 @@ class Transaction(Batch):
97
114
are not set.
98
115
"""
99
116
117
+ _INITIAL = 0
118
+ """Enum value for _INITIAL status of transaction."""
119
+
120
+ _IN_PROGRESS = 1
121
+ """Enum value for _IN_PROGRESS status of transaction."""
122
+
123
+ _ABORTED = 2
124
+ """Enum value for _ABORTED status of transaction."""
125
+
126
+ _FINISHED = 3
127
+ """Enum value for _FINISHED status of transaction."""
128
+
100
129
def __init__ (self , dataset_id = None , connection = None ):
101
130
super (Transaction , self ).__init__ (dataset_id , connection )
102
131
self ._id = None
132
+ self ._status = self ._INITIAL
103
133
104
134
@property
105
135
def id (self ):
@@ -129,7 +159,12 @@ def begin(self):
129
159
This method is called automatically when entering a with
130
160
statement, however it can be called explicitly if you don't want
131
161
to use a context manager.
162
+
163
+ :raises: :class:`ValueError` if the transaction has already begun.
132
164
"""
165
+ if self ._status != self ._INITIAL :
166
+ raise ValueError ('Transaction already started previously.' )
167
+ self ._status = self ._IN_PROGRESS
133
168
self ._id = self .connection .begin_transaction (self ._dataset_id )
134
169
135
170
def rollback (self ):
@@ -140,8 +175,12 @@ def rollback(self):
140
175
- Sets the current connection's transaction reference to None.
141
176
- Sets the current transaction's ID to None.
142
177
"""
143
- self .connection .rollback (self ._dataset_id , self ._id )
144
- self ._id = None
178
+ try :
179
+ self .connection .rollback (self ._dataset_id , self ._id )
180
+ finally :
181
+ self ._status = self ._ABORTED
182
+ # Clear our own ID in case this gets accidentally reused.
183
+ self ._id = None
145
184
146
185
def commit (self ):
147
186
"""Commits the transaction.
@@ -154,7 +193,9 @@ def commit(self):
154
193
155
194
- Sets the current transaction's ID to None.
156
195
"""
157
- super (Transaction , self ).commit ()
158
-
159
- # Clear our own ID in case this gets accidentally reused.
160
- self ._id = None
196
+ try :
197
+ super (Transaction , self ).commit ()
198
+ finally :
199
+ self ._status = self ._FINISHED
200
+ # Clear our own ID in case this gets accidentally reused.
201
+ self ._id = None
0 commit comments