diff --git a/github/MainClass.py b/github/MainClass.py index ff180ef4..a5d31524 100644 --- a/github/MainClass.py +++ b/github/MainClass.py @@ -95,10 +95,34 @@ class Github(object): @property def rate_limiting(self): """ + First value is requests remaining, second value is request limit. :type: (int, int) """ + remaining, limit = self.__requester.rate_limiting + if limit < 0: + self.__requester.requestJsonAndCheck( + 'GET', + '/rate_limit', + None, + None + ) return self.__requester.rate_limiting + @property + def rate_limiting_resettime(self): + """ + Unix timestamp indicating when rate limiting will reset. + :type: int + """ + if self.__requester.rate_limiting_resettime == 0: + self.__requester.requestJsonAndCheck( + 'GET', + '/rate_limit', + None, + None + ) + return self.__requester.rate_limiting_resettime + @property def oauth_scopes(self): """ diff --git a/github/Requester.py b/github/Requester.py index ca263e0a..26d5d6c8 100644 --- a/github/Requester.py +++ b/github/Requester.py @@ -87,7 +87,8 @@ class Requester: self.__connectionClass = self.__httpConnectionClass else: assert False, "Unknown URL scheme" - self.rate_limiting = (5000, 5000) + self.rate_limiting = (-1, -1) + self.rate_limiting_resettime = 0 self.FIX_REPO_GET_GIT_REF = True self.per_page = per_page @@ -175,6 +176,8 @@ class Requester: if "x-ratelimit-remaining" in responseHeaders and "x-ratelimit-limit" in responseHeaders: self.rate_limiting = (int(responseHeaders["x-ratelimit-remaining"]), int(responseHeaders["x-ratelimit-limit"])) + if "x-ratelimit-reset" in responseHeaders: + self.rate_limiting_resettime = int(responseHeaders["x-ratelimit-reset"]) if "x-oauth-scopes" in responseHeaders: self.oauth_scopes = responseHeaders["x-oauth-scopes"].split(", ") diff --git a/github/tests/RateLimiting.py b/github/tests/RateLimiting.py index 602dfc5f..b45ffbf6 100644 --- a/github/tests/RateLimiting.py +++ b/github/tests/RateLimiting.py @@ -31,3 +31,4 @@ class RateLimiting(Framework.TestCase): self.assertEqual(self.g.rate_limiting, (5000, 5000)) self.g.get_user("jacquev6") self.assertEqual(self.g.rate_limiting, (4999, 5000)) + self.assertEqual(self.g.rate_limiting_resettime, 1375802816) diff --git a/github/tests/ReplayData/RateLimiting.testRateLimiting.txt b/github/tests/ReplayData/RateLimiting.testRateLimiting.txt index 2fad6735..208c1716 100644 --- a/github/tests/ReplayData/RateLimiting.testRateLimiting.txt +++ b/github/tests/ReplayData/RateLimiting.testRateLimiting.txt @@ -1,3 +1,14 @@ +https +GET +api.github.com +None +/rate_limit +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '5000'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('access-control-expose-headers', 'ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes'), ('cache-control', 'max-age=0, private, must-revalidate'), ('vary', 'Accept-Encoding'), ('content-length', '59'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('etag', '"47ba6b48c8b2986ec54f249b51b0a9ec"'), ('access-control-allow-credentials', 'true'), ('date', 'Tue, 06 Aug 2013 14:52:12 GMT'), ('x-oauth-scopes', 'user, public_repo, repo, gist'), ('content-type', 'application/json; charset=utf-8'), ('access-control-allow-origin', '*'), ('x-ratelimit-reset', '1375802816')] +{"rate":{"limit":5000,"remaining":5000,"reset":1375802816}} + https GET api.github.com @@ -6,6 +17,6 @@ None {'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} null 200 -[('status', '200 OK'), ('x-ratelimit-remaining', '4999'), ('content-length', '801'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"8cd01ddcd0adee5501abc607e6a640b0"'), ('date', 'Mon, 21 May 2012 11:08:21 GMT'), ('content-type', 'application/json; charset=utf-8')] -{"total_private_repos":5,"public_gists":1,"type":"User","owned_private_repos":5,"private_gists":5,"company":"Criteo","url":"https://api.github.com/users/jacquev6","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","plan":{"private_repos":5,"collaborators":1,"name":"micro","space":614400},"followers":13,"blog":"http://vincent-jacques.net","collaborators":0,"login":"jacquev6","email":"vincent@vincent-jacques.net","disk_usage":16812,"public_repos":11,"html_url":"https://github.com/jacquev6","name":"Vincent Jacques","hireable":false,"created_at":"2010-07-09T06:10:06Z","location":"Paris, France","id":327146,"following":24,"bio":""} +[('status', '200 OK'), ('x-ratelimit-remaining', '4999'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('access-control-expose-headers', 'ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes'), ('access-control-allow-credentials', 'true'), ('vary', 'Accept, Authorization, Cookie, Accept-Encoding'), ('content-length', '1293'), ('server', 'GitHub.com'), ('access-control-allow-origin', '*'), ('last-modified', 'Mon, 05 Aug 2013 07:28:42 GMT'), ('x-ratelimit-limit', '5000'), ('etag', '"7d9b8600b27332ec98f57ee9e18639e9"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('date', 'Tue, 06 Aug 2013 14:52:12 GMT'), ('x-oauth-scopes', 'user, public_repo, repo, gist'), ('content-type', 'application/json; charset=utf-8'), ('x-accepted-oauth-scopes', 'user, user:email, user:follow, site_admin'), ('x-ratelimit-reset', '1375802816')] +{"login":"jacquev6","id":327146,"avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","url":"https://api.github.com/users/jacquev6","html_url":"https://github.com/jacquev6","followers_url":"https://api.github.com/users/jacquev6/followers","following_url":"https://api.github.com/users/jacquev6/following{/other_user}","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","organizations_url":"https://api.github.com/users/jacquev6/orgs","repos_url":"https://api.github.com/users/jacquev6/repos","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","received_events_url":"https://api.github.com/users/jacquev6/received_events","type":"User","name":"Vincent Jacques","company":"","blog":"http://vincent-jacques.net","location":"Paris, France","email":"vincent@vincent-jacques.net","hireable":false,"bio":"","public_repos":16,"followers":27,"following":39,"created_at":"2010-07-09T06:10:06Z","updated_at":"2013-08-05T07:28:42Z","public_gists":3}