Skip to content

Commit 06c2fc5

Browse files
anandswaminathandanielhochman
authored andcommitted
prepare request with requests session so session properties are applied (#197)
1 parent ce741ab commit 06c2fc5

File tree

2 files changed

+66
-4
lines changed

2 files changed

+66
-4
lines changed

pynamodb/connection/base.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
ITEMS, DEFAULT_ENCODING, BINARY_SHORT, BINARY_SET_SHORT, LAST_EVALUATED_KEY, RESPONSES, UNPROCESSED_KEYS,
3838
UNPROCESSED_ITEMS, STREAM_SPECIFICATION, STREAM_VIEW_TYPE, STREAM_ENABLED)
3939
from pynamodb.settings import get_settings_value
40+
from botocore.vendored.requests import Request
4041

4142
BOTOCORE_EXCEPTIONS = (BotoCoreError, ClientError)
4243

@@ -228,6 +229,25 @@ def _log_error(self, operation, response):
228229
log.error("%s failed with status: %s, message: %s",
229230
operation, response.status_code,response.content)
230231

232+
def _create_prepared_request(self, request_dict, operation_model):
233+
"""
234+
Create a prepared request object from request_dict, and operation_model
235+
"""
236+
boto_prepared_request = self.client._endpoint.create_request(request_dict, operation_model)
237+
238+
# The call requests_session.send(final_prepared_request) ignores the headers which are
239+
# part of the request session. In order to include the requests session headers inside
240+
# the request, we create a new request object, and call prepare_request with the newly
241+
# created request object
242+
raw_request_with_params = Request(
243+
boto_prepared_request.method,
244+
boto_prepared_request.url,
245+
data=boto_prepared_request.body,
246+
headers=boto_prepared_request.headers
247+
)
248+
249+
return self.requests_session.prepare_request(raw_request_with_params)
250+
231251
def dispatch(self, operation_name, operation_kwargs):
232252
"""
233253
Dispatches `operation_name` with arguments `operation_kwargs`
@@ -259,7 +279,8 @@ def _make_api_call(self, operation_name, operation_kwargs):
259279
operation_kwargs,
260280
operation_model
261281
)
262-
prepared_request = self.client._endpoint.create_request(request_dict, operation_model)
282+
283+
prepared_request = self._create_prepared_request(request_dict, operation_model)
263284

264285
for i in range(0, self._max_retry_attempts_exception + 1):
265286
attempt_number = i + 1

pynamodb/tests/test_base_connection.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,42 @@ def test_make_api_call_throws_verbose_error_after_backoff_later_succeeds(self, r
16951695

16961696
assert rand_int_mock.call_args_list == [mock.call(0, 25), mock.call(0, 50)]
16971697

1698+
1699+
@mock.patch('pynamodb.connection.Connection.session')
1700+
@mock.patch('pynamodb.connection.Connection.requests_session')
1701+
def test_create_prepared_request(self, requests_session_mock, session_mock):
1702+
prepared_request = requests.Request('POST',
1703+
'http://lyft.com',
1704+
data='data',
1705+
headers={'s': 's'}).prepare()
1706+
mock_client = session_mock.create_client.return_value
1707+
mock_client._endpoint.create_request.return_value = prepared_request
1708+
1709+
c = Connection()
1710+
c._max_retry_attempts_exception = 3
1711+
c._create_prepared_request({'x': 'y'}, {'a': 'b'})
1712+
1713+
self.assertEqual(len(requests_session_mock.mock_calls), 1)
1714+
1715+
self.assertEqual(requests_session_mock.mock_calls[0][:2][0],
1716+
'prepare_request')
1717+
1718+
called_request_object = requests_session_mock.mock_calls[0][:2][1][0]
1719+
expected_request_object = requests.Request(prepared_request.method,
1720+
prepared_request.url,
1721+
data=prepared_request.body,
1722+
headers=prepared_request.headers)
1723+
1724+
self.assertEqual(len(mock_client._endpoint.create_request.mock_calls), 1)
1725+
self.assertEqual(mock_client._endpoint.create_request.mock_calls[0],
1726+
mock.call({'x': 'y'}, {'a': 'b'}))
1727+
1728+
self.assertEqual(called_request_object.method, expected_request_object.method)
1729+
self.assertEqual(called_request_object.url, expected_request_object.url)
1730+
self.assertEqual(called_request_object.data, expected_request_object.data)
1731+
self.assertEqual(called_request_object.headers, expected_request_object.headers)
1732+
1733+
16981734
@mock.patch('pynamodb.connection.Connection.session')
16991735
@mock.patch('pynamodb.connection.Connection.requests_session')
17001736
def test_make_api_call_retries_properly(self, requests_session_mock, session_mock):
@@ -1707,7 +1743,6 @@ def test_make_api_call_retries_properly(self, requests_session_mock, session_moc
17071743
bad_response.status_code = 503
17081744

17091745
prepared_request = requests.Request('GET', 'http://lyft.com').prepare()
1710-
session_mock.create_client.return_value._endpoint.create_request.return_value = prepared_request
17111746

17121747
requests_session_mock.send.side_effect = [
17131748
bad_response,
@@ -1717,9 +1752,12 @@ def test_make_api_call_retries_properly(self, requests_session_mock, session_moc
17171752
]
17181753
c = Connection()
17191754
c._max_retry_attempts_exception = 3
1755+
c._create_prepared_request = mock.Mock()
1756+
c._create_prepared_request.return_value = prepared_request
17201757

17211758
c._make_api_call('DescribeTable', {'TableName': 'MyTable'})
17221759
self.assertEqual(len(requests_session_mock.mock_calls), 4)
1760+
17231761
for call in requests_session_mock.mock_calls:
17241762
self.assertEqual(call[:2], ('send', (prepared_request,)))
17251763

@@ -1728,7 +1766,6 @@ def test_make_api_call_retries_properly(self, requests_session_mock, session_moc
17281766
@mock.patch('pynamodb.connection.Connection.requests_session')
17291767
def test_make_api_call_throws_when_retries_exhausted(self, requests_session_mock, session_mock):
17301768
prepared_request = requests.Request('GET', 'http://lyft.com').prepare()
1731-
session_mock.create_client.return_value._endpoint.create_request.return_value = prepared_request
17321769

17331770
requests_session_mock.send.side_effect = [
17341771
requests.ConnectionError('problems!'),
@@ -1738,6 +1775,8 @@ def test_make_api_call_throws_when_retries_exhausted(self, requests_session_mock
17381775
]
17391776
c = Connection()
17401777
c._max_retry_attempts_exception = 3
1778+
c._create_prepared_request = mock.Mock()
1779+
c._create_prepared_request.return_value = prepared_request
17411780

17421781
with self.assertRaises(requests.Timeout):
17431782
c._make_api_call('DescribeTable', {'TableName': 'MyTable'})
@@ -1753,12 +1792,14 @@ def test_make_api_call_throws_when_retries_exhausted(self, requests_session_mock
17531792
@mock.patch('pynamodb.connection.Connection.requests_session')
17541793
def test_make_api_call_throws_retry_disabled(self, requests_session_mock, session_mock, rand_int_mock):
17551794
prepared_request = requests.Request('GET', 'http://lyft.com').prepare()
1756-
session_mock.create_client.return_value._endpoint.create_request.return_value = prepared_request
17571795

17581796
requests_session_mock.send.side_effect = [
17591797
requests.Timeout('problems!'),
17601798
]
17611799
c = Connection(request_timeout_seconds=11, base_backoff_ms=3, max_retry_attempts=0)
1800+
c._create_prepared_request = mock.Mock()
1801+
c._create_prepared_request.return_value = prepared_request
1802+
17621803
assert c._base_backoff_ms == 3
17631804
with self.assertRaises(requests.Timeout):
17641805
c._make_api_call('DescribeTable', {'TableName': 'MyTable'})

0 commit comments

Comments
 (0)