mirror of
https://github.com/josegonzalez/python-github-backup.git
synced 2025-12-06 16:38:03 +01:00
Retry API requests which failed due to rate-limiting
This allows operation to continue, albeit at a slower pace, if you have enough data to trigger the API rate limits
This commit is contained in:
@@ -4,6 +4,7 @@ from __future__ import print_function
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import base64
|
import base64
|
||||||
|
import calendar
|
||||||
import errno
|
import errno
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@@ -12,6 +13,7 @@ import re
|
|||||||
import select
|
import select
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import urllib
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
@@ -161,20 +163,45 @@ def retrieve_data(args, template, query_args=None, single_request=False):
|
|||||||
request = urllib2.Request(template + '?' + querystring)
|
request = urllib2.Request(template + '?' + querystring)
|
||||||
if auth is not None:
|
if auth is not None:
|
||||||
request.add_header('Authorization', 'Basic ' + auth)
|
request.add_header('Authorization', 'Basic ' + auth)
|
||||||
r = urllib2.urlopen(request)
|
|
||||||
|
|
||||||
errors = []
|
errors = []
|
||||||
if int(r.getcode()) != 200:
|
|
||||||
errors.append('Bad response from api')
|
|
||||||
|
|
||||||
if 'X-RateLimit-Limit' in r.headers and int(r.headers['X-RateLimit-Limit']) == 0:
|
# We'll make requests in a loop so we can delay and retry in the case of rate-limiting
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
r = urllib2.urlopen(request)
|
||||||
|
except urllib2.HTTPError as exc:
|
||||||
|
# HTTPError behaves like a Response so we can check the status code and headers to see exactly
|
||||||
|
# what failed.
|
||||||
|
|
||||||
|
limit_remaining = int(exc.headers.get('x-ratelimit-remaining', 0))
|
||||||
|
|
||||||
|
if exc.code == 403 and limit_remaining < 1:
|
||||||
|
# The X-RateLimit-Reset header includes a timestamp telling us when the limit will reset
|
||||||
|
# so we can calculate how long to wait rather than inefficiently polling:
|
||||||
|
gm_now = calendar.timegm(time.gmtime())
|
||||||
|
reset = int(exc.headers.get('x-ratelimit-reset', 0)) or gm_now
|
||||||
|
# We'll never sleep for less than 10 seconds:
|
||||||
|
delta = max(10, reset - gm_now)
|
||||||
|
|
||||||
|
limit = exc.headers.get('x-ratelimit-limit')
|
||||||
|
print('Exceeded rate limit of {} requests; waiting {} seconds to reset'.format(limit, delta),
|
||||||
|
file=sys.stderr)
|
||||||
|
|
||||||
ratelimit_error = 'No more requests remaining'
|
ratelimit_error = 'No more requests remaining'
|
||||||
if auth is None:
|
if auth is None:
|
||||||
ratelimit_error = ratelimit_error + ', specify username/password or token to raise your github ratelimit'
|
ratelimit_error = ratelimit_error + '; authenticate to raise your GitHub rate limit'
|
||||||
|
|
||||||
errors.append(ratelimit_error)
|
errors.append(ratelimit_error)
|
||||||
|
|
||||||
if int(r.getcode()) != 200:
|
time.sleep(delta)
|
||||||
|
continue
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
status_code = int(r.getcode())
|
||||||
|
|
||||||
|
if status_code != 200:
|
||||||
|
errors.append('API request returned HTTP {}: {}'.format(status_code, r.reason))
|
||||||
log_error(errors)
|
log_error(errors)
|
||||||
|
|
||||||
response = json.loads(r.read())
|
response = json.loads(r.read())
|
||||||
|
|||||||
Reference in New Issue
Block a user