diff --git a/SPARQLWrapper/SPARQLExceptions.py b/SPARQLWrapper/SPARQLExceptions.py index b811248..4df85d0 100644 --- a/SPARQLWrapper/SPARQLExceptions.py +++ b/SPARQLWrapper/SPARQLExceptions.py @@ -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 and )." + if retry_after: + self.msg += "Retry-After header value="+retry_after + + super(TooManyRequests, self).__init__(response) + self.retry_after = retry_after diff --git a/SPARQLWrapper/Wrapper.py b/SPARQLWrapper/Wrapper.py index dddaa82..7baf9fc 100644 --- a/SPARQLWrapper/Wrapper.py +++ b/SPARQLWrapper/Wrapper.py @@ -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 @@ -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() @@ -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: diff --git a/test/wrapper_test.py b/test/wrapper_test.py index f539382..f4446bb 100644 --- a/test/wrapper_test.py +++ b/test/wrapper_test.py @@ -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): @@ -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 @@ -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()