diff --git a/HISTORY.rst b/HISTORY.rst index b3842e6..bc2e278 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,11 +3,13 @@ History ------- -4.1.0 +4.1.0 (2020-09-25) ++++++++++++++++++ * Added the ``is_residential_proxy`` attribute to ``geoip2.model.AnonymousIP`` and ``geoip2.record.Traits``. +* ``HTTPError`` now provides the decoded response content in the + ``decoded_content`` attribute. Requested by Oleg Serbokryl. GitHub #95. 4.0.2 (2020-07-28) ++++++++++++++++++ diff --git a/geoip2/errors.py b/geoip2/errors.py index d3d40d9..a1f65f2 100644 --- a/geoip2/errors.py +++ b/geoip2/errors.py @@ -32,15 +32,21 @@ class HTTPError(GeoIP2Error): :ivar http_status: The HTTP status code returned :ivar uri: The URI queried + :ivar decoded_content: The decoded response content """ def __init__( - self, message: str, http_status: Optional[int] = None, uri: Optional[str] = None + self, + message: str, + http_status: Optional[int] = None, + uri: Optional[str] = None, + decoded_content: Optional[str] = None, ) -> None: super().__init__(message) self.http_status = http_status self.uri = uri + self.decoded_content = decoded_content class InvalidRequestError(GeoIP2Error): diff --git a/geoip2/webservice.py b/geoip2/webservice.py index 2a01a7b..32a1e67 100644 --- a/geoip2/webservice.py +++ b/geoip2/webservice.py @@ -112,8 +112,8 @@ def _exception_for_error( if 400 <= status < 500: return self._exception_for_4xx_status(status, content_type, body, uri) if 500 <= status < 600: - return self._exception_for_5xx_status(status, uri) - return self._exception_for_non_200_status(status, uri) + return self._exception_for_5xx_status(status, uri, body) + return self._exception_for_non_200_status(status, uri, body) def _exception_for_4xx_status( self, status: int, content_type: str, body: str, uri: str @@ -123,6 +123,7 @@ def _exception_for_4xx_status( "Received a %(status)i error for %(uri)s " "with no body." % locals(), status, uri, + body, ) if content_type.find("json") == -1: return HTTPError( @@ -130,6 +131,7 @@ def _exception_for_4xx_status( "body: %s" % (status, uri, str(content_type)), status, uri, + body, ) try: decoded_body = json.loads(body) @@ -139,9 +141,10 @@ def _exception_for_4xx_status( " not include the expected JSON body: " % locals() + ", ".join(ex.args), status, uri, + body, ) else: - if "code" in body and "error" in body: + if "code" in decoded_body and "error" in decoded_body: return self._exception_for_web_service_error( decoded_body.get("error"), decoded_body.get("code"), status, uri ) @@ -149,6 +152,7 @@ def _exception_for_4xx_status( "Response contains JSON but it does not specify " "code or error keys", status, uri, + body, ) @staticmethod @@ -180,20 +184,26 @@ def _exception_for_web_service_error( return InvalidRequestError(message, code, status, uri) @staticmethod - def _exception_for_5xx_status(status: int, uri: str) -> HTTPError: + def _exception_for_5xx_status( + status: int, uri: str, body: Optional[str] + ) -> HTTPError: return HTTPError( "Received a server error (%(status)i) for " "%(uri)s" % locals(), status, uri, + body, ) @staticmethod - def _exception_for_non_200_status(status: int, uri: str) -> HTTPError: + def _exception_for_non_200_status( + status: int, uri: str, body: Optional[str] + ) -> HTTPError: return HTTPError( "Received a very surprising HTTP status " "(%(status)i) for %(uri)s" % locals(), status, uri, + body, )