From 8065c30948299cc1029fa4f92c49ab411c38bfb5 Mon Sep 17 00:00:00 2001 From: Josh Mervine Date: Wed, 17 Dec 2014 12:54:19 -0800 Subject: [PATCH 1/4] Returning response._content when json parse fails. --- Makefile | 3 +++ maxcdn/maxcdn.py | 26 +++++++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index f511343..6c8c4bc 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,9 @@ init: clean setup test setup: distribute pip install -r requirements.txt -t $(target) -b $(source) +console: + $(pypath) python + clean: rm -rf $(source) $(target) .ropeproject .coverage junit-report.xml find . -type f -name "*.pyc" -exec rm -v {} \; diff --git a/maxcdn/maxcdn.py b/maxcdn/maxcdn.py index d03e957..04fc993 100644 --- a/maxcdn/maxcdn.py +++ b/maxcdn/maxcdn.py @@ -1,18 +1,18 @@ from requests_oauthlib import OAuth1Session as OAuth1 -from os import environ as env try: import urlparse -except ImportError: # handly python 3.x +except ImportError: # handle python 3.x from urllib import parse as urlparse + class MaxCDN(object): def __init__(self, alias, key, secret, server="rws.maxcdn.com", **kwargs): - self.url = "https://%s/%s" % (server, alias) + self.url = "https://%s/%s" % (server, alias) self.client = OAuth1(key, client_secret=secret, **kwargs) def _get_headers(self, json=True): - headers = { "User-Agent": "Python MaxCDN API Client" } + headers = {"User-Agent": "Python MaxCDN API Client"} if json: headers["Content-Type"] = "application/json" return headers @@ -30,14 +30,23 @@ def _data_request(self, method, end_point, data, **kwargs): params = urlparse.parse_qs(params) data = params - return getattr(self.client, method)(self._get_url(end_point), + response = getattr(self.client, method)(self._get_url(end_point), data=data, headers=self._get_headers(json=True), - **kwargs).json() + **kwargs) + + try: + return response.json() + except ValueError: + return response._content def get(self, end_point, **kwargs): - return self.client.get(self._get_url(end_point), + response = self.client.get(self._get_url(end_point), headers=self._get_headers(json=False), - **kwargs).json() + **kwargs) + try: + return response.json() + except ValueError: + return response._content def patch(self, end_point, data=None, **kwargs): return self._data_request("post", end_point, data=data, **kwargs) @@ -57,4 +66,3 @@ def purge(self, zoneid, file_or_files=None, **kwargs): return self.delete(path, data = { "files": file_or_files }, **kwargs) return self.delete(path, **kwargs) - From eb23fd236f8d3324c170529c9b41f52467c39fc4 Mon Sep 17 00:00:00 2001 From: Josh Mervine Date: Wed, 17 Dec 2014 15:06:36 -0800 Subject: [PATCH 2/4] Adding custom ValueError on bad json parse. --- maxcdn/maxcdn.py | 17 ++++++++--------- test/test.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/maxcdn/maxcdn.py b/maxcdn/maxcdn.py index 04fc993..5b231c8 100644 --- a/maxcdn/maxcdn.py +++ b/maxcdn/maxcdn.py @@ -23,6 +23,12 @@ def _get_url(self, end_point): else: return self.url + end_point + def _parse_json(self, response): + try: + return response.json() + except ValueError, e: + raise ValueError(e.message + ". Raw response was:\n\n" + response._content) + def _data_request(self, method, end_point, data, **kwargs): if data is None and "params" in kwargs: params = kwargs.pop("params") @@ -33,20 +39,13 @@ def _data_request(self, method, end_point, data, **kwargs): response = getattr(self.client, method)(self._get_url(end_point), data=data, headers=self._get_headers(json=True), **kwargs) - - try: - return response.json() - except ValueError: - return response._content + return self._parse_json(response) def get(self, end_point, **kwargs): response = self.client.get(self._get_url(end_point), headers=self._get_headers(json=False), **kwargs) - try: - return response.json() - except ValueError: - return response._content + return self._parse_json(response) def patch(self, end_point, data=None, **kwargs): return self._data_request("post", end_point, data=data, **kwargs) diff --git a/test/test.py b/test/test.py index bf77268..00cffa0 100644 --- a/test/test.py +++ b/test/test.py @@ -15,9 +15,19 @@ def __init__(self, method, code=200, data={"foo":"bar"}): def json(self): return self._json +class ErrorResponse(object): + def __init__(self, code=200, badcontent="Some bad content."): + self._content = badcontent + + def json(self): + raise ValueError("No JSON object could be decoded") + def response(method, **kwargs): return Response(method, **kwargs) +def error_response(**kwargs): + return ErrorResponse(**kwargs) + # ################################################################################ @@ -48,12 +58,22 @@ def test_data_request(self): self.assertEqual(self.maxcdn._data_request(meth, meth+".json", data={"foo":"bar"}), { "code": 200, "method": meth, "data": { "foo":"bar" } }) + requests.Session.request = mock.create_autospec(mock_request, + return_value=error_response()) + with self.assertRaises(ValueError): + self.maxcdn._data_request(meth, meth+".json", data={"foo":"bar"}) + def test_get(self): requests.Session.request = mock.create_autospec(mock_request, return_value=response("get")) self.assertEqual(self.maxcdn.get("/get.json"), { "code": 200, "method": "get", "data": { "foo":"bar" } }) + requests.Session.request = mock.create_autospec(mock_request, + return_value=error_response()) + with self.assertRaises(ValueError): + self.maxcdn.get("/get.json") + def test_post(self): requests.Session.request = mock.create_autospec(mock_request, return_value=response("post")) From a8d5301be031177c1867805c11067b5cc3497cbc Mon Sep 17 00:00:00 2001 From: Josh Mervine Date: Thu, 18 Dec 2014 10:39:40 -0800 Subject: [PATCH 3/4] Adding server error handling. --- maxcdn/maxcdn.py | 32 ++++++++++++++++++++++++++++++-- test/test.py | 16 ++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/maxcdn/maxcdn.py b/maxcdn/maxcdn.py index 6ac5884..4dbabe1 100644 --- a/maxcdn/maxcdn.py +++ b/maxcdn/maxcdn.py @@ -1,4 +1,5 @@ from requests_oauthlib import OAuth1Session as OAuth1 +from pprint import pprint # handle python 3.x try: @@ -28,8 +29,7 @@ def _parse_json(self, response): try: return response.json() except ValueError, e: - raise ValueError(e.message + ". Raw response was:\n\n" - + response._content) + raise self.ServerError(response, e.message) def _data_request(self, method, end_point, data, **kwargs): if data is None and "params" in kwargs: @@ -40,6 +40,10 @@ def _data_request(self, method, end_point, data, **kwargs): action = getattr(self.client, method) response = action(self._get_url(end_point), data=data, headers=self._get_headers(json=True), **kwargs) + + if (response.status_code > 299): + raise self.ServerError(response) + return self._parse_json(response) def get(self, end_point, data=None, **kwargs): @@ -62,3 +66,27 @@ def purge(self, zoneid, file_or_files=None, **kwargs): if file_or_files is not None: return self.delete(path, data={"files": file_or_files}, **kwargs) return self.delete(path, **kwargs) + + class ServerError(Exception): + def __init__(self, response, message=None): + try: + resp = response.json() + if message is None: + message = (resp['error']['type'] + ":: " + + resp['error']['message']) + self.reason = resp['error']['type'] + except ValueError: + if message is None: + message = (str(response.status_code) + " " + + response.reason + " from " + + response.url) + + self.reason = response.reason + + self.headers = response.headers + self.code = response.status_code + self.body = response._content + self.url = response.url + + super(Exception, self).__init__(message) + diff --git a/test/test.py b/test/test.py index 5dca594..5849bb4 100644 --- a/test/test.py +++ b/test/test.py @@ -12,26 +12,38 @@ def mock_request(method, url, *args, **kwargs): class Response(object): def __init__(self, method, code=200, data={"foo": "bar"}): self._json = {"code": code, "method": method, "data": data} + self.status_code = code + self.reason = "Success" + self.headers = "" + self.url = "http://www.example.com/foo/bar" def json(self): return self._json + class ErrorResponse(object): def __init__(self, code=200, badcontent="Some bad content."): self._content = badcontent + self.status_code = code + self.reason = "Error Response" + self.headers = "" + self.url = "http://www.example.com/foo/bar" def json(self): raise ValueError("No JSON object could be decoded") + def response(method, **kwargs): return Response(method, **kwargs) + def error_response(**kwargs): return ErrorResponse(**kwargs) # ############################################################################### + class MaxCDNTests(unittest.TestCase): def setUp(self): @@ -66,7 +78,7 @@ def test_data_request(self): requests.Session.request = mock.create_autospec(mock_request, return_value=error_response()) - with self.assertRaises(ValueError): + with self.assertRaises(MaxCDN.ServerError): self.maxcdn._data_request(meth, meth+".json", data={"foo":"bar"}) def test_get(self): @@ -78,7 +90,7 @@ def test_get(self): requests.Session.request = mock.create_autospec(mock_request, return_value=error_response()) - with self.assertRaises(ValueError): + with self.assertRaises(MaxCDN.ServerError): self.maxcdn.get("/get.json") def test_post(self): From b0af4ba2797060dfdbf47681d97e1d7e886c1114 Mon Sep 17 00:00:00 2001 From: Josh Mervine Date: Thu, 18 Dec 2014 11:30:03 -0800 Subject: [PATCH 4/4] Fixing 3.x issues, removing 2.6 from official support. --- .travis.yml | 1 - maxcdn/maxcdn.py | 18 ++++++++---------- test/test.py | 4 ++-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 786447e..35252f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "2.6" - "2.7" - "3.2" - "3.3" diff --git a/maxcdn/maxcdn.py b/maxcdn/maxcdn.py index 4dbabe1..08e129e 100644 --- a/maxcdn/maxcdn.py +++ b/maxcdn/maxcdn.py @@ -1,5 +1,4 @@ from requests_oauthlib import OAuth1Session as OAuth1 -from pprint import pprint # handle python 3.x try: @@ -21,15 +20,15 @@ def _get_headers(self, json=True): def _get_url(self, end_point): if not end_point.startswith("/"): - return "%s/%s" % (self.url, end_point) + return "{0}/{1}".format(self.url, end_point) else: return self.url + end_point def _parse_json(self, response): try: return response.json() - except ValueError, e: - raise self.ServerError(response, e.message) + except ValueError as e: + raise self.ServerError(response, str(e)) def _data_request(self, method, end_point, data, **kwargs): if data is None and "params" in kwargs: @@ -72,14 +71,14 @@ def __init__(self, response, message=None): try: resp = response.json() if message is None: - message = (resp['error']['type'] + ":: " - + resp['error']['message']) + message = "{0}:: {1}".format(resp['error']['type'], + resp['error']['message']) self.reason = resp['error']['type'] except ValueError: if message is None: - message = (str(response.status_code) + " " - + response.reason + " from " - + response.url) + message = "{0} {1} from {2}".format(response.status_code, + response.reason, + response.url) self.reason = response.reason @@ -89,4 +88,3 @@ def __init__(self, response, message=None): self.url = response.url super(Exception, self).__init__(message) - diff --git a/test/test.py b/test/test.py index 5849bb4..7b4275a 100644 --- a/test/test.py +++ b/test/test.py @@ -78,7 +78,7 @@ def test_data_request(self): requests.Session.request = mock.create_autospec(mock_request, return_value=error_response()) - with self.assertRaises(MaxCDN.ServerError): + with self.assertRaises(MaxCDN.ServerError, None): self.maxcdn._data_request(meth, meth+".json", data={"foo":"bar"}) def test_get(self): @@ -90,7 +90,7 @@ def test_get(self): requests.Session.request = mock.create_autospec(mock_request, return_value=error_response()) - with self.assertRaises(MaxCDN.ServerError): + with self.assertRaises(MaxCDN.ServerError, None): self.maxcdn.get("/get.json") def test_post(self):