Source code for globus_sdk.response
import logging
logger = logging.getLogger(__name__)
[docs]class GlobusResponse:
"""
Generic response object, with a single ``data`` member.
The most common response data is a JSON dictionary. To make
handling this type of response as seemless as possible, the
``GlobusResponse`` object also supports direct dictionary item
access, as an alias for accessing an item of the underlying
``data``. If ``data`` is not a dictionary, item access will raise
``TypeError``.
>>> print("Response ID": r["id"]) # alias for r.data["id"]
``GlobusResponse`` objects *always* wrap some kind of data to
return to a caller. Most basic actions on a GlobusResponse are
just ways of interacting with this data.
"""
def __init__(self, data, client=None):
# TODO: In v2, consider making client a required argument
# client is the originating client object, which can be used by
# advanced response classes to implement fancy methods which need to
# interact with the client
self._client = client
self._data = data
def __str__(self):
return repr(self)
def __repr__(self):
return f"{self.__class__.__name__}({self.data!r})"
def __getitem__(self, key):
# force evaluation of the data property outside of the upcoming
# try-catch so that we don't accidentally catch TypeErrors thrown
# during the getter function itself
data = self.data
try:
return data[key]
except TypeError:
logger.error("Can't index into responses of type {}".format(type(self)))
# re-raise with an altered message -- the issue is that whatever
# type of GlobusResponse you're working with doesn't support
# indexing
# "type" is ambiguous, but we don't know if it's the fault of the
# class at large, or just a particular call's `data` property
raise TypeError(
"This type of GlobusResponse object does not support indexing."
)
def __contains__(self, item):
"""
``x in GlobusResponse`` is an alias for ``x in GlobusResponse.data``
"""
return item in self.data
@property
def data(self):
"""
Response data as a Python data structure. Usually a dict or
list.
"""
return self._data
[docs] def get(self, *args, **kwargs):
"""
``GlobusResponse.get`` is just an alias for ``GlobusResponse.data.get``
"""
return self.data.get(*args, **kwargs)
[docs]class GlobusHTTPResponse(GlobusResponse):
"""
Response object that wraps an HTTP response from the underlying HTTP
library. If the response is JSON, the parsed data will be available in
``data``, otherwise ``data`` will be ``None`` and ``text`` should
be used instead.
:ivar http_status: HTTP status code returned by the server (int)
:ivar content_type: Content-Type header returned by the server (str)
"""
def __init__(self, http_response, client=None):
# the API response as some form of HTTP response object will be the
# underlying data of an API response
GlobusResponse.__init__(self, http_response, client=client)
# NB: the word 'code' is confusing because we use it in the
# error body, and status_code is not much better. http_code, or
# http_status_code if we wanted to be really explicit, is
# clearer, but less consistent with requests (for better and
# worse).
self.http_status = http_response.status_code
self.content_type = http_response.headers.get("Content-Type")
@property
def data(self):
try:
return self._data.json()
# JSON decoding may raise a ValueError due to an invalid JSON
# document. In the case of trying to fetch the "data" on an HTTP
# response, this means we didn't get a JSON response. Rather than
# letting the error bubble up, return None, as in "no data"
# if the caller *really* wants the raw body of the response, they can
# always use text_body
except ValueError:
logger.warning(
"GlobusHTTPResponse.data is null when body is not valid JSON"
)
return None
@property
def text(self):
"""
The raw response data as a string.
"""
return self._data.text