diff --git a/b2sdk/_internal/exception.py b/b2sdk/_internal/exception.py index aef410f3..d692a778 100644 --- a/b2sdk/_internal/exception.py +++ b/b2sdk/_internal/exception.py @@ -433,6 +433,12 @@ class ServiceError(TransientErrorMixin, B2Error): Used for HTTP status codes 500 through 599. """ + def __init__(self, *args, retry_after_seconds=None): + super().__init__(*args) + + if retry_after_seconds is not None: + self.retry_after_seconds = int(retry_after_seconds) + class CapExceeded(B2Error): def __str__(self): @@ -753,5 +759,8 @@ def interpret_b2_error( elif status == 429: return TooManyRequests(retry_after_seconds=response_headers.get('retry-after')) elif 500 <= status < 600: - return ServiceError('%d %s %s' % (status, code, message)) + return ServiceError( + '%d %s %s' % (status, code, message), + retry_after_seconds=response_headers.get('retry-after'), + ) return UnknownError('%d %s %s' % (status, code, message)) diff --git a/changelog.d/606.fixed.md b/changelog.d/606.fixed.md new file mode 100644 index 00000000..bd710ff3 --- /dev/null +++ b/changelog.d/606.fixed.md @@ -0,0 +1 @@ +Fixed honoring the Retry-After header on 5xx service errors. diff --git a/test/unit/test_exception.py b/test/unit/test_exception.py index 258e301a..f4f6e8c9 100644 --- a/test/unit/test_exception.py +++ b/test/unit/test_exception.py @@ -147,6 +147,15 @@ def test_too_many_requests_without_retry_after_header(self): error = self._check_one(TooManyRequests, 429, '', '', {}) assert error.retry_after_seconds is None + def test_service_error_with_retry_after_header(self): + retry_after = 200 + error = self._check_one(ServiceError, 503, '', '', {'retry-after': retry_after}) + assert error.retry_after_seconds == retry_after + + def test_service_error_without_retry_after_header(self): + error = self._check_one(ServiceError, 503, '', '', {}) + assert error.retry_after_seconds is None + @pytest.mark.apiver( from_ver=3 ) # previous apivers throw this as well, but BucketIdNotFound is a different class in them