Skip to content

Commit 6623b31

Browse files
author
Ilya Gurov
authored
feat(api-core): pass retry from result() to done() (googleapis#9)
Towards: googleapis/python-bigquery#24
1 parent ede6dc6 commit 6623b31

File tree

2 files changed

+62
-10
lines changed

2 files changed

+62
-10
lines changed

google/api_core/future/polling.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,18 @@ def done(self, retry=DEFAULT_RETRY):
7878
# pylint: disable=redundant-returns-doc, missing-raises-doc
7979
raise NotImplementedError()
8080

81-
def _done_or_raise(self):
81+
def _done_or_raise(self, retry=DEFAULT_RETRY):
8282
"""Check if the future is done and raise if it's not."""
83-
if not self.done():
83+
kwargs = {} if retry is DEFAULT_RETRY else {"retry": retry}
84+
85+
if not self.done(**kwargs):
8486
raise _OperationNotComplete()
8587

8688
def running(self):
8789
"""True if the operation is currently running."""
8890
return not self.done()
8991

90-
def _blocking_poll(self, timeout=None):
92+
def _blocking_poll(self, timeout=None, retry=DEFAULT_RETRY):
9193
"""Poll and wait for the Future to be resolved.
9294
9395
Args:
@@ -101,13 +103,14 @@ def _blocking_poll(self, timeout=None):
101103
retry_ = self._retry.with_deadline(timeout)
102104

103105
try:
104-
retry_(self._done_or_raise)()
106+
kwargs = {} if retry is DEFAULT_RETRY else {"retry": retry}
107+
retry_(self._done_or_raise)(**kwargs)
105108
except exceptions.RetryError:
106109
raise concurrent.futures.TimeoutError(
107110
"Operation did not complete within the designated " "timeout."
108111
)
109112

110-
def result(self, timeout=None):
113+
def result(self, timeout=None, retry=DEFAULT_RETRY):
111114
"""Get the result of the operation, blocking if necessary.
112115
113116
Args:
@@ -122,7 +125,8 @@ def result(self, timeout=None):
122125
google.api_core.GoogleAPICallError: If the operation errors or if
123126
the timeout is reached before the operation completes.
124127
"""
125-
self._blocking_poll(timeout=timeout)
128+
kwargs = {} if retry is DEFAULT_RETRY else {"retry": retry}
129+
self._blocking_poll(timeout=timeout, **kwargs)
126130

127131
if self._exception is not None:
128132
# pylint: disable=raising-bad-type

tests/unit/future/test_polling.py

+52-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import mock
2020
import pytest
2121

22-
from google.api_core import exceptions
22+
from google.api_core import exceptions, retry
2323
from google.api_core.future import polling
2424

2525

@@ -43,6 +43,8 @@ def test_polling_future_constructor():
4343
assert not future.cancelled()
4444
assert future.running()
4545
assert future.cancel()
46+
with mock.patch.object(future, "done", return_value=True):
47+
future.result()
4648

4749

4850
def test_set_result():
@@ -87,7 +89,7 @@ def __init__(self):
8789
self.poll_count = 0
8890
self.event = threading.Event()
8991

90-
def done(self):
92+
def done(self, retry=polling.DEFAULT_RETRY):
9193
self.poll_count += 1
9294
self.event.wait()
9395
self.set_result(42)
@@ -108,7 +110,7 @@ def test_result_with_polling():
108110

109111

110112
class PollingFutureImplTimeout(PollingFutureImplWithPoll):
111-
def done(self):
113+
def done(self, retry=polling.DEFAULT_RETRY):
112114
time.sleep(1)
113115
return False
114116

@@ -130,7 +132,7 @@ def __init__(self, errors):
130132
super(PollingFutureImplTransient, self).__init__()
131133
self._errors = errors
132134

133-
def done(self):
135+
def done(self, retry=polling.DEFAULT_RETRY):
134136
if self._errors:
135137
error, self._errors = self._errors[0], self._errors[1:]
136138
raise error("testing")
@@ -192,3 +194,49 @@ def test_double_callback_background_thread():
192194
assert future.poll_count == 1
193195
callback.assert_called_once_with(future)
194196
callback2.assert_called_once_with(future)
197+
198+
199+
class PollingFutureImplWithoutRetry(PollingFutureImpl):
200+
def done(self):
201+
return True
202+
203+
def result(self):
204+
return super(PollingFutureImplWithoutRetry, self).result()
205+
206+
def _blocking_poll(self, timeout):
207+
return super(PollingFutureImplWithoutRetry, self)._blocking_poll(
208+
timeout=timeout
209+
)
210+
211+
212+
class PollingFutureImplWith_done_or_raise(PollingFutureImpl):
213+
def done(self):
214+
return True
215+
216+
def _done_or_raise(self):
217+
return super(PollingFutureImplWith_done_or_raise, self)._done_or_raise()
218+
219+
220+
def test_polling_future_without_retry():
221+
custom_retry = retry.Retry(
222+
predicate=retry.if_exception_type(exceptions.TooManyRequests)
223+
)
224+
future = PollingFutureImplWithoutRetry()
225+
assert future.done()
226+
assert future.running()
227+
assert future.result() is None
228+
229+
with mock.patch.object(future, "done") as done_mock:
230+
future._done_or_raise()
231+
done_mock.assert_called_once_with()
232+
233+
with mock.patch.object(future, "done") as done_mock:
234+
future._done_or_raise(retry=custom_retry)
235+
done_mock.assert_called_once_with(retry=custom_retry)
236+
237+
238+
def test_polling_future_with__done_or_raise():
239+
future = PollingFutureImplWith_done_or_raise()
240+
assert future.done()
241+
assert future.running()
242+
assert future.result() is None

0 commit comments

Comments
 (0)