aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBu Sun Kim <8822365+busunkim96@users.noreply.github.com>2021-08-31 12:08:42 -0600
committerGitHub <noreply@github.com>2021-08-31 14:08:42 -0400
commit618f19201af729205892fcecd9c8e315ba3174a3 (patch)
tree5237847118c65fc70e0309195bda0ec060fdee1a
parent82ca2fd3792e20bea20ba96273fde4f2bb07b497 (diff)
downloadpython-api-core-618f19201af729205892fcecd9c8e315ba3174a3.tar.gz
fix: do not error on LROs with no response or error (#258)
Co-authored-by: Tres Seaver <tseaver@palladion.com>
-rw-r--r--google/api_core/operation.py10
-rw-r--r--google/api_core/operation_async.py10
-rw-r--r--tests/asyncio/test_operation_async.py6
-rw-r--r--tests/unit/test_operation.py6
4 files changed, 15 insertions, 17 deletions
diff --git a/google/api_core/operation.py b/google/api_core/operation.py
index b17f753..a66e4a5 100644
--- a/google/api_core/operation.py
+++ b/google/api_core/operation.py
@@ -140,11 +140,11 @@ class Operation(polling.PollingFuture):
)
self.set_exception(exception)
else:
- exception = exceptions.GoogleAPICallError(
- "Unexpected state: Long-running operation had neither "
- "response nor error set."
- )
- self.set_exception(exception)
+ # Some APIs set `done: true`, with an empty response.
+ # Set the result to an empty message of the expected
+ # result type.
+ # https://google.aip.dev/151
+ self.set_result(self._result_type())
def _refresh_and_update(self, retry=polling.DEFAULT_RETRY):
"""Refresh the operation and update the result if needed.
diff --git a/google/api_core/operation_async.py b/google/api_core/operation_async.py
index 6bae865..17624d6 100644
--- a/google/api_core/operation_async.py
+++ b/google/api_core/operation_async.py
@@ -136,11 +136,11 @@ class AsyncOperation(async_future.AsyncFuture):
)
self.set_exception(exception)
else:
- exception = exceptions.GoogleAPICallError(
- "Unexpected state: Long-running operation had neither "
- "response nor error set."
- )
- self.set_exception(exception)
+ # Some APIs set `done: true`, with an empty response.
+ # Set the result to an empty message of the expected
+ # result type.
+ # https://google.aip.dev/151
+ self.set_result(self._result_type())
async def _refresh_and_update(self, retry=async_future.DEFAULT_RETRY):
"""Refresh the operation and update the result if needed.
diff --git a/tests/asyncio/test_operation_async.py b/tests/asyncio/test_operation_async.py
index 907cda7..342184f 100644
--- a/tests/asyncio/test_operation_async.py
+++ b/tests/asyncio/test_operation_async.py
@@ -153,7 +153,7 @@ async def test_exception():
@mock.patch("asyncio.sleep", autospec=True)
@pytest.mark.asyncio
-async def test_unexpected_result(unused_sleep):
+async def test_done_with_no_error_or_response(unused_sleep):
responses = [
make_operation_proto(),
# Second operation response is done, but has not error or response.
@@ -161,9 +161,9 @@ async def test_unexpected_result(unused_sleep):
]
future, _, _ = make_operation_future(responses)
- exception = await future.exception()
+ result = await future.result()
- assert "Unexpected state" in "{!r}".format(exception)
+ assert isinstance(result, struct_pb2.Struct)
def test_from_gapic():
diff --git a/tests/unit/test_operation.py b/tests/unit/test_operation.py
index 28fbfe2..7a3e3c6 100644
--- a/tests/unit/test_operation.py
+++ b/tests/unit/test_operation.py
@@ -163,7 +163,7 @@ def test_exception_with_error_code():
assert isinstance(exception, exceptions.NotFound)
-def test_unexpected_result():
+def test_done_with_no_error_or_response():
responses = [
make_operation_proto(),
# Second operation response is done, but has not error or response.
@@ -171,9 +171,7 @@ def test_unexpected_result():
]
future, _, _ = make_operation_future(responses)
- exception = future.exception()
-
- assert "Unexpected state" in "{!r}".format(exception)
+ assert isinstance(future.result(), struct_pb2.Struct)
def test__refresh_http():