Repository: googleapis/google-cloud-python

Issue:
make_exception fails when json.loads fails
See [report](https://github.com/GoogleCloudPlatform/gcloud-python/issues/616#issuecomment-74173944) from @pdknsk



Discussion:
@pdknsk Can you execute this code and give the output:

``` python
from gcloud import datastore
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb

datastore.set_defaults()

# Create whatever entity you used with datastore.put([...]) in your
# stacktrace

batch = datastore.Batch()
batch.put(entity)

request_pb = datastore_pb.CommitRequest()
request_pb.mode = datastore_pb.CommitRequest.NON_TRANSACTIONAL
request_pb.mutation.CopyFrom(batch.mutation)

data = request_pb.SerializeToString()

headers = {
    'Content-Type': 'application/x-protobuf',
    'Content-Length': str(len(data)),
    'User-Agent': 'gcloud/0.4.0-bug-test',
}

api_url = batch.connection.build_api_url(
    dataset_id=batch.dataset_id, method='commit')

new_headers, content = batch.connection.http.request(
    uri=api_url, method='POST', headers=headers, body=data)

print 'new_headers:'
print new_headers

print 'content:'
print content
```

Either that or you could just provide the could you used to create the entity that caused the failure and I can handle from there.

``` Python
>>> print new_headers
{'status': '400', 'alternate-protocol': '443:quic,p=0.08', 'content-length': '24', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'transfer-encoding': 'chunked', 'expires': 'Thu, 12 Feb 2015 23:42:34 GMT', 'vary': 'Origin, X-Origin', 'server': 'GSE', '-content-encoding': 'gzip', 'cache-control': 'private, max-age=0', 'date': 'Thu, 12 Feb 2015 23:42:34 GMT', 'x-frame-options': 'SAMEORIGIN', 'content-type': 'text/html; charset=UTF-8'}
>>> print content
Entity value is indexed.
```

OK that's simple to fix.

Thanks.


Resolving patch:
diff --git a/gcloud/datastore/connection.py b/gcloud/datastore/connection.py
--- a/gcloud/datastore/connection.py
+++ b/gcloud/datastore/connection.py
@@ -67,7 +67,7 @@ def _request(self, dataset_id, method, data):
 
         status = headers['status']
         if status != '200':
-            raise make_exception(headers, content)
+            raise make_exception(headers, content, use_json=False)
 
         return content
 
diff --git a/gcloud/exceptions.py b/gcloud/exceptions.py
--- a/gcloud/exceptions.py
+++ b/gcloud/exceptions.py
@@ -155,18 +155,35 @@ class ServiceUnavailable(ServerError):
     code = 503
 
 
-def make_exception(response, content):
+def make_exception(response, content, use_json=True):
     """Factory:  create exception based on HTTP response code.
 
+    :type response: :class:`httplib2.Response` or other HTTP response object
+    :param response: A response object that defines a status code as the
+                     status attribute.
+
+    :type content: string or dictionary
+    :param content: The body of the HTTP error response.
+
+    :type use_json: boolean
+    :param use_json: Flag indicating if ``content`` is expected to be JSON.
+
     :rtype: instance of :class:`GCloudError`, or a concrete subclass.
+    :returns: Exception specific to the error response.
     """
+    message = content
+    errors = ()
 
     if isinstance(content, str):
-        content = json.loads(content)
+        if use_json:
+            payload = json.loads(content)
+        else:
+            payload = {}
+    else:
+        payload = content
 
-    message = content.get('message')
-    error = content.get('error', {})
-    errors = error.get('errors', ())
+    message = payload.get('message', message)
+    errors = payload.get('error', {}).get('errors', ())
 
     try:
         klass = _HTTP_CODE_TO_EXCEPTION[response.status]


Test patch:
diff --git a/gcloud/datastore/test_connection.py b/gcloud/datastore/test_connection.py
--- a/gcloud/datastore/test_connection.py
+++ b/gcloud/datastore/test_connection.py
@@ -106,10 +106,10 @@ def test__request_not_200(self):
         METHOD = 'METHOD'
         DATA = 'DATA'
         conn = self._makeOne()
-        conn._http = Http({'status': '400'}, '{"message": "Bad Request"}')
+        conn._http = Http({'status': '400'}, 'Entity value is indexed.')
         with self.assertRaises(BadRequest) as e:
             conn._request(DATASET_ID, METHOD, DATA)
-        expected_message = ('400 Bad Request')
+        expected_message = '400 Entity value is indexed.'
         self.assertEqual(str(e.exception), expected_message)
 
     def test__rpc(self):