Skip to content

Commit 36b241f

Browse files
Merge pull request #88 from 3lnc/client_refactor
Client refactor
2 parents fa72b74 + 8812835 commit 36b241f

File tree

3 files changed

+60
-36
lines changed

3 files changed

+60
-36
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ matrix:
1515
fail_fast: true
1616
before_install:
1717
- pip install pycodestyle
18+
- if [[ "$TRAVIS_PYTHON_VERSION" == 2.7* ]]; then pip install mock; fi
1819
install:
1920
- pip install codecov
2021
- travis_retry pip install coverage

python_http_client/client.py

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""HTTP Client library"""
22
import json
3+
34
from .exceptions import handle_error
45

56
try:
@@ -62,6 +63,9 @@ def to_dict(self):
6263
class Client(object):
6364
"""Quickly and easily access any REST or REST-like API."""
6465

66+
# These are the supported HTTP verbs
67+
methods = {'delete', 'get', 'patch', 'post', 'put'}
68+
6569
def __init__(self,
6670
host,
6771
request_headers=None,
@@ -88,8 +92,6 @@ def __init__(self,
8892
self._version = version
8993
# _url_path keeps track of the dynamically built url
9094
self._url_path = url_path or []
91-
# These are the supported HTTP verbs
92-
self.methods = ['delete', 'get', 'patch', 'post', 'put']
9395
# APPEND SLASH set
9496
self.append_slash = append_slash
9597
self.timeout = timeout
@@ -211,44 +213,51 @@ def get_version(*args, **kwargs):
211213
if name in self.methods:
212214
method = name.upper()
213215

214-
def http_request(*_, **kwargs):
216+
def http_request(
217+
request_body=None,
218+
query_params=None,
219+
request_headers=None,
220+
timeout=None,
221+
**_):
215222
"""Make the API call
216-
:param args: unused
223+
:param timeout: HTTP request timeout. Will be propagated to
224+
urllib client
225+
:type timeout: float
226+
:param request_headers: HTTP headers. Will be merged into
227+
current client object state
228+
:type request_headers: dict
229+
:param query_params: HTTP query parameters
230+
:type query_params: dict
231+
:param request_body: HTTP request body
232+
:type request_body: string or json-serializable object
217233
:param kwargs:
218-
:return: Client object
234+
:return: Response object
219235
"""
220-
if 'request_headers' in kwargs:
221-
self._update_headers(kwargs['request_headers'])
222-
if 'request_body' not in kwargs:
236+
if request_headers:
237+
self._update_headers(request_headers)
238+
239+
if request_body is None:
223240
data = None
224241
else:
225242
# Don't serialize to a JSON formatted str
226243
# if we don't have a JSON Content-Type
227-
if 'Content-Type' in self.request_headers:
228-
if self.request_headers['Content-Type'] != \
229-
'application/json':
230-
data = kwargs['request_body'].encode('utf-8')
231-
else:
232-
data = json.dumps(
233-
kwargs['request_body']).encode('utf-8')
244+
if 'Content-Type' in self.request_headers and \
245+
self.request_headers['Content-Type'] != \
246+
'application/json':
247+
data = request_body.encode('utf-8')
234248
else:
235-
data = json.dumps(
236-
kwargs['request_body']).encode('utf-8')
237-
238-
if 'query_params' in kwargs:
239-
params = kwargs['query_params']
240-
else:
241-
params = None
249+
self.request_headers.setdefault(
250+
'Content-Type', 'application/json')
251+
data = json.dumps(request_body).encode('utf-8')
242252

243253
opener = urllib.build_opener()
244-
request = urllib.Request(self._build_url(params), data=data)
245-
if self.request_headers:
246-
for key, value in self.request_headers.items():
247-
request.add_header(key, value)
248-
if data and ('Content-Type' not in self.request_headers):
249-
request.add_header('Content-Type', 'application/json')
254+
request = urllib.Request(
255+
self._build_url(query_params),
256+
headers=self.request_headers,
257+
data=data,
258+
)
250259
request.get_method = lambda: method
251-
timeout = kwargs.pop('timeout', None)
260+
252261
return Response(
253262
self._make_request(opener, request, timeout=timeout)
254263
)

tests/test_unit.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@
33

44
from python_http_client.client import Client
55
from python_http_client.exceptions import (
6-
handle_error,
7-
HTTPError,
8-
BadRequestsError,
6+
BadRequestsError, HTTPError,
97
NotFoundError,
8+
ServiceUnavailableError,
109
UnsupportedMediaTypeError,
11-
ServiceUnavailableError
10+
handle_error
1211
)
1312

1413
try:
1514
# Python 3
1615
import urllib.request as urllib
16+
from unittest import mock
1717
except ImportError:
1818
# Python 2
1919
import urllib2 as urllib
20+
import mock
2021

2122
try:
2223
basestring
@@ -81,7 +82,7 @@ def test__init__(self):
8182
self.assertEqual(default_client.host, self.host)
8283
self.assertEqual(default_client.request_headers, {})
8384
self.assertIs(default_client.timeout, None)
84-
methods = ['delete', 'get', 'patch', 'post', 'put']
85+
methods = {'delete', 'get', 'patch', 'post', 'put'}
8586
self.assertEqual(default_client.methods, methods)
8687
self.assertIsNone(default_client._version)
8788
self.assertEqual(default_client._url_path, [])
@@ -94,7 +95,7 @@ def test__init__(self):
9495
timeout=10)
9596
self.assertEqual(client.host, self.host)
9697
self.assertEqual(client.request_headers, request_headers)
97-
methods = ['delete', 'get', 'patch', 'post', 'put']
98+
methods = {'delete', 'get', 'patch', 'post', 'put'}
9899
self.assertEqual(client.methods, methods)
99100
self.assertEqual(client._version, 3)
100101
self.assertEqual(client._url_path, [])
@@ -120,6 +121,19 @@ def test__build_url(self):
120121
built_url = self.client._build_url(query_params)
121122
self.assertEqual(built_url, url)
122123

124+
@mock.patch('python_http_client.client.Client._make_request')
125+
def test__urllib_headers(self, maker):
126+
self.client._update_headers({'X-test': 'Test'})
127+
self.client.get()
128+
request = maker.call_args[0][1]
129+
self.assertIn('X-test', request.headers)
130+
131+
@mock.patch('python_http_client.client.Client._make_request')
132+
def test__urllib_method(self, maker):
133+
self.client.delete()
134+
request = maker.call_args[0][1]
135+
self.assertEqual(request.get_method(), 'DELETE')
136+
123137
def test__update_headers(self):
124138
request_headers = {'X-Test': 'Test'}
125139
self.client._update_headers(request_headers)
@@ -148,7 +162,7 @@ def test__getattr__(self):
148162
self.assertEqual(client._version, 3)
149163

150164
# Test GET
151-
mock_client._url_path + ['test']
165+
mock_client._url_path += ['test']
152166
r = mock_client.get()
153167
self.assertEqual(r.status_code, 200)
154168

0 commit comments

Comments
 (0)