Skip to content

Commit

Permalink
improve exception reporting (#41)
Browse files Browse the repository at this point in the history
use 'error' attribute from response in string representation of exception
  • Loading branch information
wtfrank authored and hkraal committed Aug 18, 2016
1 parent e8c86e2 commit 9b541ec
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 24 deletions.
22 changes: 21 additions & 1 deletion pycrest/errors.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,22 @@
class APIException(Exception):
pass
def __init__(self, url, code, json_response):

self.url = url
self.status_code = code
self.response = json_response

def __str__(self):
if 'error' in self.response:
return 'HTTP Error %s: %s' % (self.status_code, self.response['error'])
elif 'message' in self.response:
return 'HTTP Error %s: %s' % (self.status_code, self.response['message'])
else:
return 'HTTP Error %s' % (self.status_code)


class UnsupportedHTTPMethodException(Exception):
def __init__(self, method):
self.method = method

def __str__(self):
return 'Unsupported HTTP Method: %s' % (self.method)
41 changes: 30 additions & 11 deletions pycrest/eve.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pickle
from pycrest import version
from pycrest.compat import bytes_, text_
from pycrest.errors import APIException
from pycrest.errors import APIException, UnsupportedHTTPMethodException

try:
from urllib.parse import urlparse, urlunparse, parse_qsl
Expand Down Expand Up @@ -207,10 +207,14 @@ def get(self, resource, params={}):

logger.debug('Getting resource %s (params=%s)', resource, prms)
res = self._session.get(resource, params=prms)

if res.status_code != 200:
raise APIException(
"Got unexpected status code from server: %i" %
res.status_code)
resource,
res.status_code,
res.json()
)


ret = res.json()

Expand All @@ -232,7 +236,11 @@ def post(self, resource, data={}):
logger.debug('Posting resource %s (data=%s)', resource, data)
res = self._session.post(resource, data=data)
if res.status_code not in [200, 201]:
raise APIException("Got unexpected status code from server: %i" % res.status_code)
raise APIException(
resource,
res.status_code,
res.json()
)

return {}

Expand All @@ -241,7 +249,11 @@ def put(self, resource, data={}):
logger.debug('Putting resource %s (data=%s)', resource, data)
res = self._session.put(resource, data=data)
if res.status_code != 200:
raise APIException("Got unexpected status code from server: %i" % res.status_code)
raise APIException(
resource,
res.status_code,
res.json()
)

return {}

Expand All @@ -250,7 +262,11 @@ def delete(self, resource):
logger.debug('Deleting resource %s', resource)
res = self._session.delete(resource)
if res.status_code != 200:
raise APIException("Got unexpected status code from server: %i" % res.status_code)
raise APIException(
resource,
res.status_code,
res.json()
)

return {}

Expand Down Expand Up @@ -309,15 +325,18 @@ def _authorize(self, params):
"%s:%s" %
(self.client_id, self.api_key))))
headers = {"Authorization": "Basic %s" % auth}
resource = "%s/token" % self._oauth_endpoint
res = self._session.post(
"%s/token" %
self._oauth_endpoint,
resource,
params=params,
headers=headers)
if res.status_code != 200:
raise APIException(
"Got unexpected status code from API: %i" %
res.status_code)
resource,
res.status_code,
res.json()
)

return res.json()

def authorize(self, code):
Expand Down Expand Up @@ -474,7 +493,7 @@ def __call__(self, **kwargs):
elif method == 'get':
return APIObject(self.connection.get(self._dict['href'], params=data), self.connection)
else:
raise APIException( "unhandled HTTP method: %s" % method)
raise UnsupportedHTTPMethodException(method)
else:
return self

Expand Down
64 changes: 64 additions & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from pycrest.errors import APIException, UnsupportedHTTPMethodException
import unittest

try:
import __builtin__
builtins_name = __builtin__.__name__
except ImportError:
import builtins
builtins_name = builtins.__name__



class TestAPIException(unittest.TestCase):

def setUp(self):
pass

def test_apiexception_data(self):
e = APIException('http://example.com', 205, {'message' : 'example error'})

self.assertEqual(
e.url,
'http://example.com')

self.assertEqual(
e.status_code,
205)

def test_apiexception_str_message(self):
e = APIException('http://example.com', 205, {'message' : 'example error'})

self.assertIn(
'example error',
str(e))

self.assertIn( '205', str(e) )

def test_apiexception_str_error(self):
e = APIException('http://example.com', 205, {'error' : 'example error'})

self.assertIn(
'example error',
str(e))

self.assertIn( '205', str(e) )



def test_apiexception_str_no_message(self):
e = APIException('http://example.com', 205, {'exception_type' : 'wierd'})
self.assertIn( '205', str(e) )


class TestUnsupportedHTTPMethodException(unittest.TestCase):
def setUp(self):
pass

def test_exception_str(self):
e = UnsupportedHTTPMethodException('flatten')
self.assertIn( 'flatten', str(e) )


if __name__ == "__main__":
unittest.main()
29 changes: 17 additions & 12 deletions tests/test_pycrest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import pycrest
import mock
import errno
from pycrest.errors import APIException
from pycrest.errors import APIException, UnsupportedHTTPMethodException
from requests.models import PreparedRequest
import unittest

Expand Down Expand Up @@ -86,8 +86,9 @@ def fallback_mock(url, request):
def mock_login(url, request):
return httmock.response(
status_code=200,
content='{"access_token": "access_token", "refresh_token": "r'
'efresh_token", "expires_in": 300}')
content='{"access_token": "access_token",'
' "refresh_token": "refresh_token",'
' "expires_in": 300}')


@httmock.urlmatch(
Expand All @@ -97,8 +98,11 @@ def mock_login(url, request):
def market_prices_mock(url, request):
return httmock.response(
status_code=200,
content='{"totalCount_str": "10213", "items": [], "pageCount": 1, "pa'
'geCount_str": "1", "totalCount": 10213}')
content='{"totalCount_str": "10213",'
' "items": [],'
' "pageCount": 1,'
' "pageCount_str": "1",'
' "totalCount": 10213}')

@httmock.urlmatch(
scheme="https",
Expand Down Expand Up @@ -167,7 +171,8 @@ def test_authorize_non_200(self):

@httmock.all_requests
def mock_login(url, request):
return httmock.response(status_code=204)
return httmock.response(status_code=204,
content='{}')

with httmock.HTTMock(mock_login):
self.assertRaises(APIException, self.api.authorize, code='code')
Expand Down Expand Up @@ -362,7 +367,7 @@ def test_non_http_200(self):

@httmock.all_requests
def non_http_200(url, request):
return {'status_code': 404}
return {'status_code': 404, 'content' : {'message' : 'not found'}}

with httmock.HTTMock(non_http_200):
self.assertRaises(APIException, self.api)
Expand Down Expand Up @@ -598,7 +603,7 @@ def test_non_http_200_201_post(self):

@httmock.all_requests
def non_http_200(url, request):
return {'status_code': 404}
return {'status_code': 404, 'content' : {'message' : 'not found'}}

with httmock.HTTMock(non_http_200):
self.assertRaises(APIException, self.api.writeableEndpoint, method='post')
Expand All @@ -607,7 +612,7 @@ def test_non_http_200_put(self):

@httmock.all_requests
def non_http_200(url, request):
return {'status_code': 201}
return {'status_code': 201, 'content' : {'message' : 'created new object'}}

with httmock.HTTMock(non_http_200):
self.assertRaises(APIException, self.api.writeableEndpoint, method='put')
Expand All @@ -616,7 +621,7 @@ def test_non_http_200_delete(self):

@httmock.all_requests
def non_http_200(url, request):
return {'status_code': 201}
return {'status_code': 201, 'content' : {'message' : 'created new object'}}

with httmock.HTTMock(non_http_200):
self.assertRaises(APIException, self.api.writeableEndpoint, method='delete')
Expand All @@ -625,7 +630,7 @@ def non_http_200(url, request):
def test_http_201_post(self):
@httmock.all_requests
def http_201(url, request):
return {'status_code': 201}
return {'status_code': 201, 'content' : {'message' : 'created new object'}}

with httmock.HTTMock(http_201):
res = self.api.writeableEndpoint(method='post')
Expand Down Expand Up @@ -658,7 +663,7 @@ def test_dict_parameter_passing(self):

def test_unhandled_http_method_exception(self):
with httmock.HTTMock(*all_httmocks):
self.assertRaises(APIException, self.api.writeableEndpoint, method='snip') #made-up http method
self.assertRaises(UnsupportedHTTPMethodException, self.api.writeableEndpoint, method='snip') #made-up http method

if __name__ == "__main__":
unittest.main()

0 comments on commit 9b541ec

Please sign in to comment.