Skip to content

Commit 654163e

Browse files
mikeybtnWill Myers
authored andcommitted
Add HTTPResponseError to expose underlying http error codes and causes. (#30)
* Add HTTPResponseError to expose underlying http error codes and causes. * Cut 2.6.0 release
1 parent 11fac1b commit 654163e

File tree

7 files changed

+41
-17
lines changed

7 files changed

+41
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
Current Version
2-
-
1+
2.6.0 May 17, 2018
2+
- Added `HTTPResponseError`
33

44
2.5.0 December 5, 2017
55
- Add links resource

README.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,30 @@ key <https://app.usebutton.com/settings/organization>`__.
3737
client = Client('sk-XXX')
3838
3939
The client will always attempt to raise a ``pybutton.ButtonClientError``
40-
in an error condition.
40+
or a subclass in an error condition.
4141

4242
All API requests will return a ``pybutton.response.Response`` instance,
4343
which supports accessing data via the `#data` method. For instance:
4444

4545
.. code:: python
4646
4747
from pybutton import Client
48-
from pybutton import ButtonClientError
48+
from pybutton import ButtonClientError, HTTPResponseError
4949
5050
client = Client("sk-XXX")
5151
5252
try:
5353
response = client.orders.get("btnorder-XXX")
54+
except HTTPResponseError as e:
55+
print('API request failed: http status {}'.format(e.status_code))
5456
except ButtonClientError as e:
5557
print(e)
58+
else:
59+
print(response)
60+
# <class pybutton.Response status: open, btn_ref: None, line_items: [], ...>
5661
57-
print(response)
58-
# <class pybutton.Response status: open, btn_ref: None, line_items: [], ...>
59-
60-
print(response.data())
61-
# {'status': open, 'btn_ref': None, 'line_items': [], ...}
62+
print(response.data())
63+
# {'status': open, 'btn_ref': None, 'line_items': [], ...}
6264
6365
6466
Configuration

pybutton/error.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,18 @@
77
class ButtonClientError(Exception):
88
'''An Exception class for all pybutton understood errors.
99
'''
10+
11+
12+
class HTTPResponseError(ButtonClientError):
13+
'''A non-success HTTP response was returned from the remote API.
14+
15+
The HTTP response status code can be retrieved from the
16+
`.status_code` property.
17+
18+
The original error object can be retrieved from the
19+
`.cause` property.
20+
'''
21+
def __init__(self, message, status_code, cause):
22+
super(HTTPResponseError, self).__init__(message)
23+
self.status_code = status_code
24+
self.cause = cause

pybutton/resources/resource.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import json
99

1010
from pybutton.response import Response
11-
from pybutton.error import ButtonClientError
11+
from pybutton.error import HTTPResponseError
1212
from pybutton.version import VERSION
1313
from pybutton.request import request
1414
from pybutton.request import request_url
@@ -38,6 +38,7 @@ class Resource(object):
3838
3939
Raises:
4040
pybutton.ButtonClientError
41+
pybutton.HTTPResponseError
4142
4243
'''
4344

@@ -151,4 +152,4 @@ def _api_request(self, path, method, data=None, query=None):
151152

152153
error = json.loads(data).get('error', {})
153154
message = error.get('message', fallback)
154-
raise ButtonClientError(message)
155+
raise HTTPResponseError(message, status_code=e.code, cause=e)

pybutton/test/request_test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ def test_raises_with_invalid_response_data(self, MockRequest,
103103
try:
104104
request(url, method, headers)
105105
self.assertTrue(False)
106-
except ButtonClientError:
107-
pass
106+
except ButtonClientError as e:
107+
# We expect the generic ButtonClientError, and not a subclass,
108+
# in this condition.
109+
assert type(e) is ButtonClientError
108110

109111

110112
class RequestTestCasePy3(TestCase):

pybutton/test/resources/resource_test.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from pybutton.request import HTTPError
1111
from pybutton.resources.resource import Resource
12-
from pybutton.error import ButtonClientError
12+
from pybutton.error import HTTPResponseError
1313

1414
config = {
1515
'hostname': 'api.usebutton.com',
@@ -117,8 +117,10 @@ def side_effect(*args):
117117
try:
118118
resource._api_request('/v2/api', 'GET', data)
119119
self.assertTrue(False)
120-
except ButtonClientError as e:
120+
except HTTPResponseError as e:
121121
self.assertEqual(str(e), '404 bloop')
122+
self.assertEqual(e.status_code, 404)
123+
self.assertTrue(e.cause is not None)
122124

123125
@patch('pybutton.resources.resource.request')
124126
def test_api_request_with_byte_response(self, request):
@@ -138,8 +140,10 @@ def side_effect(*args):
138140
try:
139141
resource._api_request('/v2/api', 'GET', data)
140142
self.assertTrue(False)
141-
except ButtonClientError as e:
143+
except HTTPResponseError as e:
142144
self.assertEqual(str(e), 'bloop failed')
145+
self.assertEqual(e.status_code, 404)
146+
self.assertTrue(e.cause is not None)
143147

144148
@patch('pybutton.resources.resource.request')
145149
def test_api_get(self, request):

pybutton/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = '2.5.0'
1+
VERSION = '2.6.0'

0 commit comments

Comments
 (0)