Skip to content

Commit 790ecff

Browse files
merging in master
2 parents 8bebfcc + 768b72a commit 790ecff

File tree

8 files changed

+81
-56
lines changed

8 files changed

+81
-56
lines changed

CONTRIBUTORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ Swapnil Khanapurkar <swapnil_khanapurkar@persistent.co.in>
2929
The SoftLayer Developer Network <sldn@softlayer.com>
3030
Tim Ariyeh <tim.ariyeh@gmail.com>
3131
Wissam Elriachy <wissam.elriachy@gmail.com>
32+
Anthony Monthe (ZuluPro) <anthony.monthe@gmail.com>

SoftLayer/fixtures/SoftLayer_Account.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@
266266
"statusId": 4,
267267
"accountId": 1234
268268
}
269-
]
269+
],
270+
'accountId': 1234
270271
}
271272

272273
getRwhoisData = {

SoftLayer/transports.py

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import time
1212

1313
import requests
14+
from requests.adapters import HTTPAdapter
15+
from urllib3.util.retry import Retry
1416

1517
from SoftLayer import consts
1618
from SoftLayer import exceptions
@@ -38,6 +40,20 @@
3840
}
3941

4042

43+
def get_session(user_agent):
44+
"""Sets up urllib sessions"""
45+
46+
client = requests.Session()
47+
client.headers.update({
48+
'Content-Type': 'application/json',
49+
'User-Agent': user_agent,
50+
})
51+
retry = Retry(connect=3, backoff_factor=3)
52+
adapter = HTTPAdapter(max_retries=retry)
53+
client.mount('https://', adapter)
54+
return client
55+
56+
4157
class Request(object):
4258
"""Transport request object."""
4359

@@ -107,6 +123,15 @@ def __init__(self, endpoint_url=None, timeout=None, proxy=None, user_agent=None,
107123
self.proxy = proxy
108124
self.user_agent = user_agent or consts.USER_AGENT
109125
self.verify = verify
126+
self._client = None
127+
128+
@property
129+
def client(self):
130+
"""Returns client session object"""
131+
132+
if self._client is None:
133+
self._client = get_session(self.user_agent)
134+
return self._client
110135

111136
def __call__(self, request):
112137
"""Makes a SoftLayer API call against the XML-RPC endpoint.
@@ -154,13 +179,13 @@ def __call__(self, request):
154179
LOGGER.debug(payload)
155180

156181
try:
157-
resp = requests.request('POST', url,
158-
data=payload,
159-
headers=request.transport_headers,
160-
timeout=self.timeout,
161-
verify=verify,
162-
cert=request.cert,
163-
proxies=_proxies_dict(self.proxy))
182+
resp = self.client.request('POST', url,
183+
data=payload,
184+
headers=request.transport_headers,
185+
timeout=self.timeout,
186+
verify=verify,
187+
cert=request.cert,
188+
proxies=_proxies_dict(self.proxy))
164189
LOGGER.debug("=== RESPONSE ===")
165190
LOGGER.debug(resp.headers)
166191
LOGGER.debug(resp.content)
@@ -208,6 +233,15 @@ def __init__(self, endpoint_url=None, timeout=None, proxy=None, user_agent=None,
208233
self.proxy = proxy
209234
self.user_agent = user_agent or consts.USER_AGENT
210235
self.verify = verify
236+
self._client = None
237+
238+
@property
239+
def client(self):
240+
"""Returns client session object"""
241+
242+
if self._client is None:
243+
self._client = get_session(self.user_agent)
244+
return self._client
211245

212246
def __call__(self, request):
213247
"""Makes a SoftLayer API call against the REST endpoint.
@@ -217,9 +251,6 @@ def __call__(self, request):
217251
218252
:param request request: Request object
219253
"""
220-
request.transport_headers.setdefault('Content-Type', 'application/json')
221-
request.transport_headers.setdefault('User-Agent', self.user_agent)
222-
223254
params = request.headers.copy()
224255
if request.mask:
225256
params['objectMask'] = _format_object_mask(request.mask)
@@ -275,15 +306,15 @@ def __call__(self, request):
275306
LOGGER.debug(request.transport_headers)
276307
LOGGER.debug(raw_body)
277308
try:
278-
resp = requests.request(method, url,
279-
auth=auth,
280-
headers=request.transport_headers,
281-
params=params,
282-
data=raw_body,
283-
timeout=self.timeout,
284-
verify=verify,
285-
cert=request.cert,
286-
proxies=_proxies_dict(self.proxy))
309+
resp = self.client.request(method, url,
310+
auth=auth,
311+
headers=request.transport_headers,
312+
params=params,
313+
data=raw_body,
314+
timeout=self.timeout,
315+
verify=verify,
316+
cert=request.cert,
317+
proxies=_proxies_dict(self.proxy))
287318
LOGGER.debug("=== RESPONSE ===")
288319
LOGGER.debug(resp.headers)
289320
LOGGER.debug(resp.text)

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@
3333
'six >= 1.7.0',
3434
'prettytable >= 0.7.0',
3535
'click >= 5',
36-
'requests >= 2.7.0',
36+
'requests >= 2.18.4',
3737
'prompt_toolkit >= 0.53',
3838
'pygments >= 2.0.0',
39+
'urllib3 >= 1.22'
3940
],
4041
keywords=['softlayer', 'cloud'],
4142
classifiers=[

tests/decoration_tests.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import logging
99
import mock
10-
import unittest
1110

1211
from SoftLayer.decoration import retry
1312
from SoftLayer import exceptions
@@ -89,7 +88,3 @@ def raise_unexpected_error():
8988
raise TypeError('unexpected error')
9089

9190
self.assertRaises(TypeError, raise_unexpected_error)
92-
93-
if __name__ == '__main__':
94-
95-
unittest.main()

tests/transport_tests.py

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def set_up(self):
4444
)
4545
self.response = get_xmlrpc_response()
4646

47-
@mock.patch('requests.request')
47+
@mock.patch('SoftLayer.transports.requests.Session.request')
4848
def test_call(self, request):
4949
request.return_value = self.response
5050

@@ -95,7 +95,7 @@ def test_proxy_without_protocol(self):
9595
warnings.warn("Incorrect Exception raised. Expected a "
9696
"SoftLayer.TransportError error")
9797

98-
@mock.patch('requests.request')
98+
@mock.patch('SoftLayer.transports.requests.Session.request')
9999
def test_valid_proxy(self, request):
100100
request.return_value = self.response
101101
self.transport.proxy = 'http://localhost:3128'
@@ -116,7 +116,7 @@ def test_valid_proxy(self, request):
116116
cert=None,
117117
verify=True)
118118

119-
@mock.patch('requests.request')
119+
@mock.patch('SoftLayer.transports.requests.Session.request')
120120
def test_identifier(self, request):
121121
request.return_value = self.response
122122

@@ -134,7 +134,7 @@ def test_identifier(self, request):
134134
<value><int>1234</int></value>
135135
</member>""", kwargs['data'])
136136

137-
@mock.patch('requests.request')
137+
@mock.patch('SoftLayer.transports.requests.Session.request')
138138
def test_filter(self, request):
139139
request.return_value = self.response
140140

@@ -152,7 +152,7 @@ def test_filter(self, request):
152152
<value><string>^= prefix</string></value>
153153
</member>""", kwargs['data'])
154154

155-
@mock.patch('requests.request')
155+
@mock.patch('SoftLayer.transports.requests.Session.request')
156156
def test_limit_offset(self, request):
157157
request.return_value = self.response
158158

@@ -172,7 +172,7 @@ def test_limit_offset(self, request):
172172
<value><int>10</int></value>
173173
</member>""", kwargs['data'])
174174

175-
@mock.patch('requests.request')
175+
@mock.patch('SoftLayer.transports.requests.Session.request')
176176
def test_old_mask(self, request):
177177
request.return_value = self.response
178178

@@ -194,7 +194,7 @@ def test_old_mask(self, request):
194194
</struct></value>
195195
</member>""", kwargs['data'])
196196

197-
@mock.patch('requests.request')
197+
@mock.patch('SoftLayer.transports.requests.Session.request')
198198
def test_mask_call_no_mask_prefix(self, request):
199199
request.return_value = self.response
200200

@@ -210,7 +210,7 @@ def test_mask_call_no_mask_prefix(self, request):
210210
"<value><string>mask[something.nested]</string></value>",
211211
kwargs['data'])
212212

213-
@mock.patch('requests.request')
213+
@mock.patch('SoftLayer.transports.requests.Session.request')
214214
def test_mask_call_v2(self, request):
215215
request.return_value = self.response
216216

@@ -226,7 +226,7 @@ def test_mask_call_v2(self, request):
226226
"<value><string>mask[something[nested]]</string></value>",
227227
kwargs['data'])
228228

229-
@mock.patch('requests.request')
229+
@mock.patch('SoftLayer.transports.requests.Session.request')
230230
def test_mask_call_v2_dot(self, request):
231231
request.return_value = self.response
232232

@@ -241,7 +241,7 @@ def test_mask_call_v2_dot(self, request):
241241
self.assertIn("<value><string>mask.something.nested</string></value>",
242242
kwargs['data'])
243243

244-
@mock.patch('requests.request')
244+
@mock.patch('SoftLayer.transports.requests.Session.request')
245245
def test_request_exception(self, request):
246246
# Test Text Error
247247
e = requests.HTTPError('error')
@@ -257,7 +257,7 @@ def test_request_exception(self, request):
257257
self.assertRaises(SoftLayer.TransportError, self.transport, req)
258258

259259

260-
@mock.patch('requests.request')
260+
@mock.patch('SoftLayer.transports.requests.Session.request')
261261
@pytest.mark.parametrize(
262262
"transport_verify,request_verify,expected",
263263
[
@@ -313,7 +313,7 @@ def set_up(self):
313313
endpoint_url='http://something.com',
314314
)
315315

316-
@mock.patch('requests.request')
316+
@mock.patch('SoftLayer.transports.requests.Session.request')
317317
def test_basic(self, request):
318318
request().content = '[]'
319319
request().text = '[]'
@@ -340,7 +340,7 @@ def test_basic(self, request):
340340
proxies=None,
341341
timeout=None)
342342

343-
@mock.patch('requests.request')
343+
@mock.patch('SoftLayer.transports.requests.Session.request')
344344
def test_error(self, request):
345345
# Test JSON Error
346346
e = requests.HTTPError('error')
@@ -366,10 +366,9 @@ def test_proxy_without_protocol(self):
366366
try:
367367
self.assertRaises(SoftLayer.TransportError, self.transport, req)
368368
except AssertionError:
369-
warnings.warn("AssertionError raised instead of a "
370-
"SoftLayer.TransportError error")
369+
warnings.warn("AssertionError raised instead of a SoftLayer.TransportError error")
371370

372-
@mock.patch('requests.request')
371+
@mock.patch('SoftLayer.transports.requests.Session.request')
373372
def test_valid_proxy(self, request):
374373
request().text = '{}'
375374
self.transport.proxy = 'http://localhost:3128'
@@ -392,7 +391,7 @@ def test_valid_proxy(self, request):
392391
timeout=mock.ANY,
393392
headers=mock.ANY)
394393

395-
@mock.patch('requests.request')
394+
@mock.patch('SoftLayer.transports.requests.Session.request')
396395
def test_with_id(self, request):
397396
request().text = '{}'
398397

@@ -416,7 +415,7 @@ def test_with_id(self, request):
416415
proxies=None,
417416
timeout=None)
418417

419-
@mock.patch('requests.request')
418+
@mock.patch('SoftLayer.transports.requests.Session.request')
420419
def test_with_args(self, request):
421420
request().text = '{}'
422421

@@ -440,7 +439,7 @@ def test_with_args(self, request):
440439
proxies=None,
441440
timeout=None)
442441

443-
@mock.patch('requests.request')
442+
@mock.patch('SoftLayer.transports.requests.Session.request')
444443
def test_with_filter(self, request):
445444
request().text = '{}'
446445

@@ -465,7 +464,7 @@ def test_with_filter(self, request):
465464
proxies=None,
466465
timeout=None)
467466

468-
@mock.patch('requests.request')
467+
@mock.patch('SoftLayer.transports.requests.Session.request')
469468
def test_with_mask(self, request):
470469
request().text = '{}'
471470

@@ -510,7 +509,7 @@ def test_with_mask(self, request):
510509
proxies=None,
511510
timeout=None)
512511

513-
@mock.patch('requests.request')
512+
@mock.patch('SoftLayer.transports.requests.Session.request')
514513
def test_with_limit_offset(self, request):
515514
request().text = '{}'
516515

@@ -536,7 +535,7 @@ def test_with_limit_offset(self, request):
536535
proxies=None,
537536
timeout=None)
538537

539-
@mock.patch('requests.request')
538+
@mock.patch('SoftLayer.transports.requests.Session.request')
540539
def test_unknown_error(self, request):
541540
e = requests.RequestException('error')
542541
e.response = mock.MagicMock()
@@ -550,8 +549,7 @@ def test_unknown_error(self, request):
550549

551550
self.assertRaises(SoftLayer.TransportError, self.transport, req)
552551

553-
554-
@mock.patch('requests.request')
552+
@mock.patch('SoftLayer.transports.requests.Session.request')
555553
@mock.patch('requests.auth.HTTPBasicAuth')
556554
def test_with_special_auth(self, auth, request):
557555
request().text = '{}'
@@ -565,8 +563,8 @@ def test_with_special_auth(self, auth, request):
565563
req.transport_user = user
566564
req.transport_password = password
567565

568-
569566
resp = self.transport(req)
567+
self.assertEqual(resp, {})
570568
auth.assert_called_with(user, password)
571569
request.assert_called_with(
572570
'GET',
@@ -587,23 +585,19 @@ def set_up(self):
587585
self.transport = transports.FixtureTransport()
588586

589587
def test_basic(self):
590-
591588
req = transports.Request()
592589
req.service = 'SoftLayer_Account'
593590
req.method = 'getObject'
594591
resp = self.transport(req)
595-
596-
self.assertEqual(resp['cdnAccounts'][0]['accountId'], 1234)
592+
self.assertEqual(resp['accountId'], 1234)
597593

598594
def test_no_module(self):
599-
600595
req = transports.Request()
601596
req.service = 'Doesnt_Exist'
602597
req.method = 'getObject'
603598
self.assertRaises(NotImplementedError, self.transport, req)
604599

605600
def test_no_method(self):
606-
607601
req = transports.Request()
608602
req.service = 'SoftLayer_Account'
609603
req.method = 'getObjectzzzz'

tools/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ click >= 5
33
prettytable >= 0.7.0
44
six >= 1.7.0
55
prompt_toolkit
6+
urllib3

tools/test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ pytest-cov
44
mock
55
sphinx
66
testtools
7+
urllib3

0 commit comments

Comments
 (0)