Skip to content

Commit

Permalink
Add "content" and "data" properties to Py42Response (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
alanag13 committed Jul 10, 2020
1 parent f9c8e8a commit e420df7
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 15 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The intended audience of this file is for py42 consumers -- as such, changes that don't affect
how a consumer would use the library (e.g. adding unit tests, updating documentation, etc) are not captured here.

## 1.6.2 - 2020-07-10

### Added

- `data` to `Py42Response`. This allows a developer to retrieve the full dict of the response under the `data` json node, if present, enabling the use of typical dict functions such as `get()`.

- `content` to `Py42Response`. This exposes the underlying `requests.Response.content`, which contains the fully body of the response as a byte array.

## 1.6.1 - 2020-07-09

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion src/py42/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# py42

__version__ = "1.6.1"
__version__ = "1.6.2"
36 changes: 22 additions & 14 deletions src/py42/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
class Py42Response(object):
def __init__(self, requests_response):
self._response = requests_response
self._data_root = None
self._data = None

def __getitem__(self, key):
try:
return self._data[key]
return self._data_root[key]
except TypeError as e:
data_root_type = type(self._data)
data_root_type = type(self._data_root)
message = u"The Py42Response root is of type {0}, but __getitem__ got a key of {1}, which is incompatible.".format(
data_root_type, key
)
raise Py42Error(message)

def __setitem__(self, key, value):
try:
self._data[key] = value
self._data_root[key] = value
except TypeError as e:
data_root_type = type(self._data_root)
message = u"The Py42Response root is of type {0}, but __setitem__ got a key of {1} and value of {2}, which is incompatible.".format(
Expand All @@ -31,7 +31,7 @@ def __setitem__(self, key, value):

def __iter__(self):
# looping over a Py42Response will loop through list items, dict keys, or str characters
return iter(self._data)
return iter(self._data_root)

@property
def encoding(self):
Expand Down Expand Up @@ -67,7 +67,7 @@ def raw_text(self):
@property
def text(self):
"""The more useful parts of the HTTP response dumped into a dictionary."""
return json.dumps(self._data) if type(self._data) != str else self._data
return json.dumps(self._data_root) if type(self._data_root) != str else self._data_root

@property
def url(self):
Expand All @@ -80,24 +80,32 @@ def status_code(self):
return self._response.status_code

def __str__(self):
return str(self._data)
return str(self._data_root)

def __repr__(self):
data = self._data
data = self._data_root
return u"<{} [status={}, data={}]>".format(
self.__class__.__name__, self._response.status_code, reprlib.repr(data)
)

@property
def _data(self):
def content(self):
return self._response.content

@property
def data(self):
return self._data_root

@property
def _data_root(self):
try:
if not self._data_root:
if not self._data:
response_dict = json.loads(self._response.text)
if type(response_dict) == dict:
self._data_root = response_dict.get(u"data") or response_dict
self._data = response_dict.get(u"data") or response_dict
else:
self._data_root = response_dict
self._data = response_dict
except ValueError:
self._data_root = self._response.text or u""
self._data = self._response.text or u""

return self._data_root
return self._data
38 changes: 38 additions & 0 deletions tests/sdk/test_response.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import json

import pytest
from requests import Response

Expand All @@ -17,24 +19,28 @@ class TestPy42Response(object):
@pytest.fixture
def mock_response_list_data_node(self, mocker):
mock_response = mocker.MagicMock(spec=Response)
mock_response.content = json.loads(JSON_LIST_WITH_DATA_NODE)
mock_response.text = JSON_LIST_WITH_DATA_NODE
return mock_response

@pytest.fixture
def mock_response_list_no_data_node(self, mocker):
mock_response = mocker.MagicMock(spec=Response)
mock_response.content = json.loads(JSON_LIST_NO_DATA_NODE)
mock_response.text = JSON_LIST_NO_DATA_NODE
return mock_response

@pytest.fixture
def mock_response_dict_data_node(self, mocker):
mock_response = mocker.MagicMock(spec=Response)
mock_response.content = json.loads(JSON_DICT_WITH_DATA_NODE)
mock_response.text = JSON_DICT_WITH_DATA_NODE
return mock_response

@pytest.fixture
def mock_response_dict_no_data_node(self, mocker):
mock_response = mocker.MagicMock(spec=Response)
mock_response.content = json.loads(JSON_DICT_NO_DATA_NODE)
mock_response.text = JSON_DICT_NO_DATA_NODE
return mock_response

Expand Down Expand Up @@ -152,3 +158,35 @@ def test_getitem_raises_py42_error_on_invalid_subscript(self, mock_response_not_
response = Py42Response(mock_response_not_json)
with pytest.raises(Py42Error):
response["test"]

def test_content_dict_no_data_node_returns_expected_dict(self, mock_response_dict_no_data_node):
response = Py42Response(mock_response_dict_no_data_node)
assert response.content == json.loads(JSON_DICT_NO_DATA_NODE)

def test_content_dict_data_node_returns_expected_dict(self, mock_response_dict_data_node):
response = Py42Response(mock_response_dict_data_node)
assert response.content == json.loads(JSON_DICT_WITH_DATA_NODE)

def test_content_list_no_data_node_returns_expected_list(self, mock_response_list_no_data_node):
response = Py42Response(mock_response_list_no_data_node)
assert response.content == json.loads(JSON_LIST_NO_DATA_NODE)

def test_content_list_data_node_returns_expected_list(self, mock_response_list_data_node):
response = Py42Response(mock_response_list_data_node)
assert response.content == json.loads(JSON_LIST_WITH_DATA_NODE)

def test_data_with_data_node_returns_list_items(self, mock_response_list_data_node):
response = Py42Response(mock_response_list_data_node)
assert type(response.data["item_list_key"]) == list

def test_data_with_data_node_returns_dict_keys(self, mock_response_dict_data_node):
response = Py42Response(mock_response_dict_data_node)
assert type(response.data["item_list_key"]) == dict

def test_data_no_data_node_returns_list_items_(self, mock_response_list_no_data_node):
response = Py42Response(mock_response_list_no_data_node)
assert type(response.data["item_list_key"]) == list

def test_data_no_data_node_returns_dict_keys(self, mock_response_dict_no_data_node):
response = Py42Response(mock_response_dict_no_data_node)
assert type(response.data["item_list_key"]) == dict

0 comments on commit e420df7

Please sign in to comment.