Skip to content
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

initial support for 429 TooManyRequests HTTP code #140

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions SPARQLWrapper/SPARQLExceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,16 @@ class URITooLong(SPARQLWrapperException):

msg = "the URI requested by the client is longer than the server is willing to interpret. Check if the request was sent using GET method instead of POST method."

class TooManyRequests(SPARQLWrapperException):
"""
The user has sent too many requests in a given amount of time ("rate limiting"). Usually HTTP response status code 429.
@since: 1.8.5
"""

def __init__(self, response=None, retry_after=None):
self.msg = "The user has sent too many requests in a given amount of time ('rate limiting'). Retry-After header (if present) indicates how long to wait before making a new request (possible values are <http-date> and <delay-seconds>)."
if retry_after:
self.msg += "Retry-After header value="+retry_after

super(TooManyRequests, self).__init__(response)
self.retry_after = retry_after
11 changes: 9 additions & 2 deletions SPARQLWrapper/Wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

import json
from .KeyCaseInsensitiveDict import KeyCaseInsensitiveDict
from .SPARQLExceptions import QueryBadFormed, EndPointNotFound, EndPointInternalError, Unauthorized, URITooLong
from .SPARQLExceptions import QueryBadFormed, EndPointNotFound, EndPointInternalError, Unauthorized, URITooLong, TooManyRequests
from SPARQLWrapper import __agent__

# alias
Expand Down Expand Up @@ -743,8 +743,9 @@ def _query(self):
:raises Unauthorized: If the HTTP return code is ``401``.
:raises EndPointNotFound: If the HTTP return code is ``404``.
:raises URITooLong: If the HTTP return code is ``414``.
:raises TooManyRequests: If the HTTP return code is ``429``.
:raises EndPointInternalError: If the HTTP return code is ``500``.
:raises urllib2.HTTPError: If the HTTP return code is different to ``400``, ``401``, ``404``, ``414``, ``500``.
:raises urllib2.HTTPError: If the HTTP return code is different to ``400``, ``401``, ``404``, ``414``, ``429``, ``500``.
"""
request = self._createRequest()

Expand All @@ -763,6 +764,12 @@ def _query(self):
raise Unauthorized(e.read())
elif e.code == 414:
raise URITooLong(e.read())
elif e.code == 429:
hdrs = KeyCaseInsensitiveDict(e.headers) # Case insensitive headers
if "retry-after" in hdrs:
raise TooManyRequests(e.read(), hdrs["retry-after"])
else:
raise TooManyRequests(e.read())
elif e.code == 500:
raise EndPointInternalError(e.read())
else:
Expand Down
19 changes: 15 additions & 4 deletions test/wrapper_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from SPARQLWrapper import XML, GET, POST, JSON, JSONLD, N3, TURTLE, RDF, SELECT, INSERT, RDFXML, CSV, TSV
from SPARQLWrapper import URLENCODED, POSTDIRECTLY
from SPARQLWrapper import BASIC, DIGEST
from SPARQLWrapper.Wrapper import QueryResult, QueryBadFormed, EndPointNotFound, EndPointInternalError, Unauthorized, URITooLong
from SPARQLWrapper.Wrapper import QueryResult, QueryBadFormed, EndPointNotFound, EndPointInternalError, Unauthorized, URITooLong, TooManyRequests


class FakeResult(object):
Expand All @@ -49,13 +49,12 @@ def urlopener(request):
return FakeResult(request)


def urlopener_error_generator(code):
def urlopener_error_generator(code, headers={}):
def urlopener_error(request):
raise HTTPError(request.get_full_url, code, '', {}, StringIO(''))
raise HTTPError(request.get_full_url, code, '', headers, StringIO(''))

return urlopener_error


def urlopener_check_data_encoding(request):
if isinstance(request.data, str):
raise TypeError
Expand Down Expand Up @@ -515,6 +514,18 @@ def testQuery(self):
except:
self.fail('got wrong exception')

RETRY_AFTER_VALUE = "100"
_victim.urlopener = urlopener_error_generator(429, {"Retry-After": RETRY_AFTER_VALUE})
try:
self.wrapper.query()
self.fail('should have raised exception')
except TooManyRequests as e:
# TODO: check exception-format
self.assertEquals(e.retry_after, RETRY_AFTER_VALUE)
pass
except:
self.fail('got wrong exception')

_victim.urlopener = urlopener_error_generator(500)
try:
self.wrapper.query()
Expand Down