Skip to content
Merged
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
4 changes: 3 additions & 1 deletion HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
++++++++++++++++++
Expand Down
8 changes: 7 additions & 1 deletion geoip2/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
20 changes: 15 additions & 5 deletions geoip2/webservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -123,13 +123,15 @@ 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(
"Received a %i for %s with the following "
"body: %s" % (status, uri, str(content_type)),
status,
uri,
body,
)
try:
decoded_body = json.loads(body)
Expand All @@ -139,16 +141,18 @@ 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
)
return HTTPError(
"Response contains JSON but it does not specify " "code or error keys",
status,
uri,
body,
)

@staticmethod
Expand Down Expand Up @@ -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,
)


Expand Down