Repository: googleapis/google-cloud-python

Issue:
Remove '_BrokenOperationFuture' workaround
`google.cloud.spanner.databse._BrokenOperationFuture` is a subclass of `google.gax._OperationFuture` which attempts to work around the fact that the spanner back-end sometimes returns metadata instead of the expected empty response, which causes the normal future to blow up.

Once the back-end is updated to fix this, remove `_BrokenOperationFuture`.


Discussion:
@tseaver @lukesneeringer 
Backend has been fixed to return empty response. You should be able to remove BrokenOperationFuture now.
@vkedia Thanks!

Resolving patch:
diff --git a/spanner/google/cloud/spanner/database.py b/spanner/google/cloud/spanner/database.py
--- a/spanner/google/cloud/spanner/database.py
+++ b/spanner/google/cloud/spanner/database.py
@@ -14,12 +14,10 @@
 
 """User friendly container for Cloud Spanner Database."""
 
-import functools
 import re
 
 from google.gax.errors import GaxError
 from google.gax.grpc import exc_to_code
-from google.gax import _OperationFuture
 from google.cloud.proto.spanner.admin.database.v1 import (
     spanner_database_admin_pb2 as admin_v1_pb2)
 from google.cloud.gapic.spanner.v1.spanner_client import SpannerClient
@@ -51,22 +49,6 @@
 register_type(admin_v1_pb2.UpdateDatabaseDdlMetadata)
 
 
-class _BrokenResultFuture(_OperationFuture):
-    """An _OperationFuture subclass that is permissive about type mismatches
-    in results, and simply returns an empty-ish object if they happen.
-
-    This class exists to get past a contra-spec result on
-    `update_database_ddl`; since the result is empty there is no
-    critical loss.
-    """
-    @functools.wraps(_OperationFuture.result)
-    def result(self, *args, **kwargs):
-        try:
-            return super(_BrokenResultFuture, self).result(*args, **kwargs)
-        except TypeError:
-            return self._result_type()
-
-
 class Database(object):
     """Representation of a Cloud Spanner Database.
 
@@ -280,7 +262,6 @@ def update_ddl(self, ddl_statements):
         try:
             future = api.update_database_ddl(
                 self.name, ddl_statements, '', options=options)
-            future.__class__ = _BrokenResultFuture
         except GaxError as exc:
             if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                 raise NotFound(self.name)


Test patch:
diff --git a/spanner/unit_tests/test_database.py b/spanner/unit_tests/test_database.py
--- a/spanner/unit_tests/test_database.py
+++ b/spanner/unit_tests/test_database.py
@@ -15,8 +15,6 @@
 
 import unittest
 
-import mock
-
 from google.cloud.spanner import __version__
 
 from google.cloud._testing import _GAXBaseAPI
@@ -1010,28 +1008,6 @@ class Testing(Exception):
         self.assertIs(pool._session, session)
 
 
-class TestBrokenResultFuture(unittest.TestCase):
-    def test_result_normal(self):
-        from google.gax import _OperationFuture
-        from google.cloud.spanner.database import _BrokenResultFuture
-
-        with mock.patch.object(_OperationFuture, 'result') as super_result:
-            super_result.return_value = 'foo'
-            brf = _BrokenResultFuture(object(), object(), str, object())
-            self.assertEqual(brf.result(), 'foo')
-            super_result.assert_called_once()
-
-    def test_result_valueerror(self):
-        from google.gax import _OperationFuture
-        from google.cloud.spanner.database import _BrokenResultFuture
-
-        with mock.patch.object(_OperationFuture, 'result') as super_result:
-            super_result.side_effect = TypeError
-            brf = _BrokenResultFuture(object(), object(), str, object())
-            self.assertEqual(brf.result(), '')
-            super_result.assert_called_once()
-
-
 class _Client(object):
 
     def __init__(self, project=TestDatabase.PROJECT_ID):