diff --git a/ovh/client.py b/ovh/client.py index b8fff5c..62913b1 100644 --- a/ovh/client.py +++ b/ovh/client.py @@ -34,6 +34,7 @@ - To get started with API: https://api.ovh.com/g934.first_step_with_api """ +import backoff import hashlib import json import keyword @@ -122,6 +123,8 @@ def __init__( consumer_key=None, timeout=TIMEOUT, config_file=None, + auto_retry=None, + auto_retry_on_exceptions=None, ): """ Creates a new Client. No credential check is done at this point. @@ -150,6 +153,8 @@ def __init__( :param str consumer_key: uniquely identifies :param tuple timeout: Connection and read timeout for each request :param float timeout: Same timeout for both connection and read + :param int auto_retry: Number of times backoff will retry if a call fail + :param tuple auto_retry_on_exceptions: Exceptions that need to perform an auto_retry :raises InvalidRegion: if ``endpoint`` can't be found in ``ENDPOINTS``. """ # Load a custom config file if requested @@ -187,7 +192,24 @@ def __init__( # Override default timeout self._timeout = timeout + # Set default auto_retry + self._auto_retry = auto_retry + + # Set default auto_retry_on_exceptions + if self.auto_retry_on_exceptions is None: + self._auto_retry_on_exceptions = (HTTPError, NetworkError) + else: + self._auto_retry_on_exceptions = auto_retry_on_exceptions + # high level API + def retry_call(self, *args, **kwargs): + """Perform raw query and handle auto_retry if necessary.""" + if self._auto_retry is None: + return self.call(*args, **kwargs) + else: + return backoff.on_exception(backoff.expo, + self._auto_retry_on_exceptions, + max_tries=self._auto_retry)(self.call)(*args, **kwargs) @property def time_delta(self): @@ -352,7 +374,7 @@ def get(self, _target, _need_auth=True, **kwargs): else: _target = "%s?%s" % (_target, query_string) - return self.call("GET", _target, None, _need_auth) + return self.retry_call('GET', _target, None, _need_auth) def put(self, _target, _need_auth=True, **kwargs): """ @@ -369,7 +391,7 @@ def put(self, _target, _need_auth=True, **kwargs): kwargs = self._canonicalize_kwargs(kwargs) if not kwargs: kwargs = None - return self.call("PUT", _target, kwargs, _need_auth) + return self.retry_call('PUT', _target, kwargs, _need_auth) def post(self, _target, _need_auth=True, **kwargs): """ @@ -386,7 +408,7 @@ def post(self, _target, _need_auth=True, **kwargs): kwargs = self._canonicalize_kwargs(kwargs) if not kwargs: kwargs = None - return self.call("POST", _target, kwargs, _need_auth) + return self.retry_call('POST', _target, kwargs, _need_auth) def delete(self, _target, _need_auth=True, **kwargs): """ @@ -409,7 +431,7 @@ def delete(self, _target, _need_auth=True, **kwargs): else: _target = "%s?%s" % (_target, query_string) - return self.call("DELETE", _target, None, _need_auth) + return self.retry_call('DELETE', _target, None, _need_auth) # low level helpers