From 3221122ad1eff0e96636b440d996226c737890e8 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 24 Sep 2014 18:35:45 -0400 Subject: [PATCH 1/3] Fix #136: roll back transactions on exceptions. --- gcloud/datastore/test_transaction.py | 12 ++++-------- gcloud/datastore/transaction.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/gcloud/datastore/test_transaction.py b/gcloud/datastore/test_transaction.py index 26526668407c..65c6861f1f7f 100644 --- a/gcloud/datastore/test_transaction.py +++ b/gcloud/datastore/test_transaction.py @@ -110,7 +110,6 @@ def test_context_manager_no_raise(self): self.assertEqual(xact.id(), None) def test_context_manager_w_raise(self): - # See https://github.com/GoogleCloudPlatform/gcloud-python/issues/136 class Foo(Exception): pass _DATASET = 'DATASET' @@ -125,13 +124,10 @@ class Foo(Exception): self.assertTrue(connection._xact is xact) raise Foo() except Foo: - pass # XXX - #self.assertEqual(xact.id(), None) - #self.assertEqual(connection._rolled_back, (_DATASET, 234)) - #self.assertEqual(connection._xact, None) - # XXX should *not* have committed - self.assertEqual(connection._committed, (_DATASET, mutation)) - #self.assertEqual(connection._committed, None) + self.assertEqual(xact.id(), None) + self.assertEqual(connection._rolled_back, (_DATASET, 234)) + self.assertEqual(connection._xact, None) + self.assertEqual(connection._committed, None) self.assertTrue(connection._xact is None) self.assertEqual(xact.id(), None) diff --git a/gcloud/datastore/transaction.py b/gcloud/datastore/transaction.py index 7c73af0a7371..6f6442c87b20 100644 --- a/gcloud/datastore/transaction.py +++ b/gcloud/datastore/transaction.py @@ -21,18 +21,15 @@ class Transaction(object): ... entity1.save() ... entity2.save() - To rollback a transaction if there is an error:: + By derault, the transaction is rolled back is an error:: >>> from gcloud import datastore >>> dataset = datastore.get_dataset('dataset-id', email, key_path) >>> with dataset.transaction() as t: - ... try: - ... do_some_work() - ... entity1.save() - ... except: - ... t.rollback() + ... do_some_work() + ... raise Exception() # rolls back - If the transaction isn't rolled back, + If the transaction block exists without an exception, it will commit by default. .. warning:: @@ -249,4 +246,7 @@ def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): - self.commit() + if exc_type is None: + self.commit() + else: + self.rollback() From ee88f941e903b3dbc5fceebc7347516df9e3c772 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 30 Sep 2014 14:30:59 -0400 Subject: [PATCH 2/3] Fix typo, s/derault/default. Feedback from @silvolu. --- gcloud/datastore/transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcloud/datastore/transaction.py b/gcloud/datastore/transaction.py index 6f6442c87b20..9586bc5ac8d4 100644 --- a/gcloud/datastore/transaction.py +++ b/gcloud/datastore/transaction.py @@ -21,7 +21,7 @@ class Transaction(object): ... entity1.save() ... entity2.save() - By derault, the transaction is rolled back is an error:: + By default, the transaction is rolled back is an error:: >>> from gcloud import datastore >>> dataset = datastore.get_dataset('dataset-id', email, key_path) From 498e151eca3e35a9f10a607b20b4781b3c822d93 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 1 Oct 2014 16:25:37 -0400 Subject: [PATCH 3/3] Merge upstream master. Resolve conflict on gcloud/datastore/test_transaction.py. --- gcloud/datastore/test_transaction.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/gcloud/datastore/test_transaction.py b/gcloud/datastore/test_transaction.py index 65c6861f1f7f..3a7080a3f1db 100644 --- a/gcloud/datastore/test_transaction.py +++ b/gcloud/datastore/test_transaction.py @@ -5,6 +5,7 @@ class TestTransaction(unittest2.TestCase): def _getTargetClass(self): from gcloud.datastore.transaction import Transaction + return Transaction def _makeOne(self, dataset=None): @@ -12,6 +13,7 @@ def _makeOne(self, dataset=None): def test_ctor(self): from gcloud.datastore.datastore_v1_pb2 import Mutation + _DATASET = 'DATASET' connection = _Connection() dataset = _Dataset(_DATASET, connection) @@ -49,7 +51,7 @@ def test_rollback(self): xact.begin() xact.rollback() self.assertEqual(xact.id(), None) - self.assertEqual(connection._rolled_back, (_DATASET, 234)) + self.assertEqual(connection._rolled_back, _DATASET) self.assertEqual(connection._xact, None) def test_commit_no_auto_ids(self): @@ -89,7 +91,7 @@ def test_commit_w_already(self): xact = self._makeOne(dataset) xact._mutation = mutation = object() xact.begin() - connection.transaction(()) # simulate previous commit via false-ish + connection.transaction(()) # Simulate previous commit via false-ish. xact.commit() self.assertEqual(connection._committed, None) self.assertTrue(connection._xact is None) @@ -125,14 +127,16 @@ class Foo(Exception): raise Foo() except Foo: self.assertEqual(xact.id(), None) - self.assertEqual(connection._rolled_back, (_DATASET, 234)) + self.assertEqual(connection._rolled_back, _DATASET) self.assertEqual(connection._xact, None) self.assertEqual(connection._committed, None) self.assertTrue(connection._xact is None) self.assertEqual(xact.id(), None) + def _makeKey(kind, id): from gcloud.datastore.datastore_v1_pb2 import Key + key = Key() elem = key.path_element.add() elem.kind = kind @@ -141,11 +145,14 @@ def _makeKey(kind, id): class _Dataset(object): + def __init__(self, id, connection=None): self._id = id self._connection = connection + def id(self): return self._id + def connection(self): return self._connection @@ -153,30 +160,37 @@ def connection(self): class _Connection(object): _marker = object() _begun = _rolled_back = _committed = _xact = None + def __init__(self, xact_id=123): self._xact_id = xact_id self._commit_result = _CommitResult() + def transaction(self, xact=_marker): if xact is self._marker: return self._xact self._xact = xact + def begin_transaction(self, dataset_id): self._begun = dataset_id return self._xact_id - def rollback_transaction(self, dataset_id, xact_id): - self._rolled_back = (dataset_id, xact_id) + + def rollback_transaction(self, dataset_id): + self._rolled_back = dataset_id + def commit(self, dataset_id, mutation): self._committed = (dataset_id, mutation) return self._commit_result class _CommitResult(object): + def __init__(self, *new_keys): self.insert_auto_id_key = new_keys class _Key(object): _path = None + def path(self, path): self._path = path return self @@ -184,8 +198,10 @@ def path(self, path): class _Entity(object): _marker = object() + def __init__(self): self._key = _Key() + def key(self, key=_marker): if key is self._marker: return self._key