-
Notifications
You must be signed in to change notification settings - Fork 669
Fix missing "Retry-After" header and fix integration tests #678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
When requests are throttled (HTTP response code 429), python-gitlab assumed that 'Retry-After' existed in the response headers. This is not always the case and so the request fails due to a KeyError. The change in this commit adds a rudimentary exponential backoff to the 'http_request' method, which defaults to 10 retries but can be set to -1 to retry without bound.
The integration tests failed because a test called 'decode()' on a string-type variable - the GitLabException class handles byte-to-string conversion already in its __init__. This commit removes the call to 'decode()' in the test. ``` Traceback (most recent call last): File "./tools/python_test_v4.py", line 801, in <module> assert 'Retry later' in error_message.decode() AttributeError: 'str' object has no attribute 'decode' ```
Thanks for this contribution! Looks pretty good. Any way to easily test this? (e.g. against a public GitLab instance without |
@max-wittig Hm, this is a bit difficult to test. But here's how I validated the code path - let me know what you think.
while True:
# result = self.session.send(prepped, timeout=timeout, **settings)
result = requests.get('https://httpstat.us/429')
self._check_redirects(result)
if 200 <= result.status_code < 300:
return result
if 429 == result.status_code and obey_rate_limit:
if max_retries == -1 or cur_retries < max_retries:
wait_time = 2 ** cur_retries * 0.1
if "Retry-AfterXYZ" in result.headers:
wait_time = int(result.headers["Retry-After"])
cur_retries += 1
print('DEBUG: backing off', str(wait_time))
time.sleep(wait_time)
continue Note that https://httpstat.us returns a response with the
from getpass import getpass
import gitlab
private_token = getpass('GitLab API Token: ')
gl = gitlab.Gitlab('https://gitlab.com/', private_token=private_token)
project = gl.projects.get(<some-project-id-we-own>)
project.members.list(all=True)
Thoughts/feedback on this approach? Didn't try spinning up and configuring a GitLab instance without the header since we don't really need it to test the code path. |
@srikanthchelluri Works as expected. Many thanks for the contribution 👍 Just a hint for future contributions: It's best practice to use a separate branch in your fork. Makes it easier for us to checkout the changes and easier for you to maintain your next contribution. Otherwise your master in your fork is polluted by another commit etc. |
@max-wittig This was a separate branch, no? I forked the repo into my org ( Thanks for the review and merge 👍 |
ah really. Okay weird. Nevermind. Maybe I miss-typed something and didn't see it. Not sure 😄 |
This PR does two things: a) implements an exponential backoff in the case that we are throttled and "Retry-After" isn't in the response header (this currently causes a
KeyError
), b) fix an integration test because we were decoding something that was already a string.Closes #632.