1# Copyright 2020 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""This contains common retrying helpers (retryers).
15
16We use tenacity as a general-purpose retrying library.
17
18> It [tenacity] originates from a fork of retrying which is sadly no
19> longer maintained. Tenacity isn’t api compatible with retrying but >
20> adds significant new functionality and fixes a number of longstanding bugs.
21> - https://tenacity.readthedocs.io/en/latest/index.html
22"""
23import datetime
24from typing import Any, List, Optional
25
26import tenacity
27
28# Type aliases
29timedelta = datetime.timedelta
30Retrying = tenacity.Retrying
31_retry_if_exception_type = tenacity.retry_if_exception_type
32_stop_after_delay = tenacity.stop_after_delay
33_wait_exponential = tenacity.wait_exponential
34
35
36def _retry_on_exceptions(retry_on_exceptions: Optional[List[Any]] = None):
37    # Retry on all exceptions by default
38    if retry_on_exceptions is None:
39        retry_on_exceptions = (Exception,)
40    return _retry_if_exception_type(retry_on_exceptions)
41
42
43def exponential_retryer_with_timeout(
44        *,
45        wait_min: timedelta,
46        wait_max: timedelta,
47        timeout: timedelta,
48        retry_on_exceptions: Optional[List[Any]] = None) -> Retrying:
49    return Retrying(retry=_retry_on_exceptions(retry_on_exceptions),
50                    wait=_wait_exponential(min=wait_min.total_seconds(),
51                                           max=wait_max.total_seconds()),
52                    stop=_stop_after_delay(timeout.total_seconds()),
53                    reraise=True)
54