Skip to content

Commit 86a8251

Browse files
author
Gauvain Pocentek
authored
Merge pull request #488 from siemens/feat/rate-limit
feat: obey the rate limit
2 parents 25ed8e7 + e216f06 commit 86a8251

File tree

3 files changed

+76
-16
lines changed

3 files changed

+76
-16
lines changed

docs/api-usage.rst

+23
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,26 @@ The following sample illustrates how to use a client-side certificate:
326326
327327
Reference:
328328
http://docs.python-requests.org/en/master/user/advanced/#client-side-certificates
329+
330+
Rate limits
331+
-----------
332+
333+
python-gitlab will obey the rate limit of the GitLab server by default.
334+
On receiving a 429 response (Too Many Requests), python-gitlab will sleep for the amount of time
335+
in the Retry-After header, that GitLab sends back.
336+
337+
If you don't want to wait, you can disable the rate-limiting feature, by supplying the
338+
``obey_rate_limit`` argument.
339+
340+
.. code-block:: python
341+
342+
import gitlab
343+
import requests
344+
345+
gl = gitlab.gitlab(url, token, api_version=4)
346+
gl.projects.list(all=True, obey_rate_limit=False)
347+
348+
349+
.. warning::
350+
351+
You will get an Exception, if you then go over the rate limit of your GitLab instance.

gitlab/__init__.py

+28-16
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import itertools
2424
import json
2525
import re
26+
import time
2627
import warnings
2728

2829
import requests
@@ -698,24 +699,35 @@ def copy_dict(dest, src):
698699
prepped.url = sanitized_url(prepped.url)
699700
settings = self.session.merge_environment_settings(
700701
prepped.url, {}, streamed, verify, None)
701-
result = self.session.send(prepped, timeout=timeout, **settings)
702702

703-
if 200 <= result.status_code < 300:
704-
return result
703+
# obey the rate limit by default
704+
obey_rate_limit = kwargs.get("obey_rate_limit", True)
705705

706-
try:
707-
error_message = result.json()['message']
708-
except (KeyError, ValueError, TypeError):
709-
error_message = result.content
710-
711-
if result.status_code == 401:
712-
raise GitlabAuthenticationError(response_code=result.status_code,
713-
error_message=error_message,
714-
response_body=result.content)
715-
716-
raise GitlabHttpError(response_code=result.status_code,
717-
error_message=error_message,
718-
response_body=result.content)
706+
while True:
707+
result = self.session.send(prepped, timeout=timeout, **settings)
708+
709+
if 200 <= result.status_code < 300:
710+
return result
711+
712+
if 429 == result.status_code and obey_rate_limit:
713+
wait_time = int(result.headers["Retry-After"])
714+
time.sleep(wait_time)
715+
continue
716+
717+
try:
718+
error_message = result.json()['message']
719+
except (KeyError, ValueError, TypeError):
720+
error_message = result.content
721+
722+
if result.status_code == 401:
723+
raise GitlabAuthenticationError(
724+
response_code=result.status_code,
725+
error_message=error_message,
726+
response_body=result.content)
727+
728+
raise GitlabHttpError(response_code=result.status_code,
729+
error_message=error_message,
730+
response_body=result.content)
719731

720732
def http_get(self, path, query_data={}, streamed=False, **kwargs):
721733
"""Make a GET request to the Gitlab server.

tools/python_test_v4.py

+25
Original file line numberDiff line numberDiff line change
@@ -646,3 +646,28 @@
646646

647647
# events
648648
gl.events.list()
649+
650+
# rate limit
651+
settings = gl.settings.get()
652+
settings.throttle_authenticated_api_enabled = True
653+
settings.throttle_authenticated_api_requests_per_period = 1
654+
settings.throttle_authenticated_api_period_in_seconds = 3
655+
settings.save()
656+
projects = list()
657+
for i in range(0, 20):
658+
projects.append(gl.projects.create(
659+
{'name': str(i) + "ok"}))
660+
661+
error_message = None
662+
for i in range(20, 40):
663+
try:
664+
projects.append(
665+
gl.projects.create(
666+
{'name': str(i) + 'shouldfail'}, obey_rate_limit=False))
667+
except gitlab.GitlabCreateError as e:
668+
error_message = e.error_message
669+
break
670+
assert 'Retry later' in error_message
671+
[current_project.delete() for current_project in projects]
672+
settings.throttle_authenticated_api_enabled = False
673+
settings.save()

0 commit comments

Comments
 (0)