Improve error displaying, and move error display logic into the error module.
diff --git a/apiclient/errors.py b/apiclient/errors.py
index b3a7d13..6a91e1e 100644
--- a/apiclient/errors.py
+++ b/apiclient/errors.py
@@ -11,6 +11,9 @@
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+from anyjson import simplejson
+
+
class Error(Exception):
"""Base error for this module."""
pass
@@ -19,12 +22,28 @@
class HttpError(Error):
"""HTTP data was invalid or unexpected."""
- def __init__(self, resp, detail):
+ def __init__(self, resp, content):
self.resp = resp
- self.detail = detail
+ self.content = content
- def __str__(self):
- return self.detail
+ def _get_reason(self):
+ """Calculate the reason for the error from the response content.
+ """
+ if self.resp.get('content-type', '').startswith('application/json'):
+ try:
+ data = simplejson.loads(self.content)
+ reason = data['error']['message']
+ except (ValueError, KeyError):
+ reason = self.content
+ else:
+ reason = self.resp.reason
+ return reason
+
+ def __repr__(self):
+ return '<HttpError %s "%s">' % (self.resp.status, self._get_reason())
+
+ __str__ = __repr__
+
class UnknownLinkType(Error):
diff --git a/apiclient/model.py b/apiclient/model.py
index f37a699..cd81c0b 100644
--- a/apiclient/model.py
+++ b/apiclient/model.py
@@ -99,7 +99,4 @@
return body
else:
logging.debug('Content from bad request was: %s' % content)
- if resp.get('content-type', '').startswith('application/json'):
- raise HttpError(resp, simplejson.loads(content)['error'])
- else:
- raise HttpError(resp, '%d %s' % (resp.status, resp.reason))
+ raise HttpError(resp, content)
diff --git a/tests/test_errors.py b/tests/test_errors.py
new file mode 100644
index 0000000..fbb4778
--- /dev/null
+++ b/tests/test_errors.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for errors handling
+"""
+
+__author__ = 'afshar@google.com (Ali Afshar)'
+
+
+import unittest
+import httplib2
+
+
+from apiclient.errors import HttpError
+
+
+def fake_response(data, headers):
+ return httplib2.Response(headers), data
+
+
+class Error(unittest.TestCase):
+ """Test handling of error bodies
+ """
+
+ def test_json_body(self):
+ """Test a nicely formed, expected error response
+ """
+ resp, content = fake_response(json_error_content,
+ {'status':'400', 'content-type': 'application/json'})
+ error = HttpError(resp, content)
+ self.assertEqual(str(error), '<HttpError 400 "country is required">')
+
+ def test_bad_json_body(self):
+ """Test handling of bodies with invalid json
+ """
+ resp, content = fake_response('{',
+ {'status':'400', 'content-type': 'application/json'})
+ error = HttpError(resp, content)
+ self.assertEqual(str(error), '<HttpError 400 "{">')
+
+ def test_missing_message_json_body(self):
+ """Test handling of bodies with missing expected 'message' element
+ """
+ resp, content = fake_response('{}',
+ {'status':'400', 'content-type': 'application/json'})
+ error = HttpError(resp, content)
+ self.assertEqual(str(error), '<HttpError 400 "{}">')
+
+ def test_non_json(self):
+ """Test handling of non-JSON bodies
+ """
+ resp, content = fake_response('NOT OK', {'status':'400'})
+ error = HttpError(resp, content)
+ self.assertEqual(str(error), '<HttpError 400 "Ok">')
+
+
+json_error_content = """
+{
+ "error": {
+ "errors": [
+ {
+ "domain": "global",
+ "reason": "required",
+ "message": "country is required",
+ "locationType": "parameter",
+ "location": "country"
+ }
+ ],
+ "code": 400,
+ "message": "country is required"
+ }
+}
+"""
+