Repository: ucfopen/canvasapi

Issue:
Implement 503 Handling
Upon receiving a 503 response from Canvas, the library returns a `json.decoder.JSONDecodeError` instead of something more useful.

Implement handling for 503 response.

It's probably worth implementing generic handling for all 4xx and 5xx responses.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed5202a..497c655 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 - Added Issue and Pull Request Templates
 - Added [Code of Conduct](https://github.com/ucfopen/canvasapi/blob/develop/CODE_OF_CONDUCT.md)
 - Added CODEOWNERS file
+- Added generic error handling for unhandled 4XX+ HTTP errors
 
 ### Deprecation Warnings
 
@@ -33,6 +34,7 @@
 
 - Fixed an issue where creating an external tool did not properly send parameters to Canvas. (Thanks, [@altgilbers](https://github.com/altgilbers))
 - Fixed an issue where getting Quiz Submissions would only return up to the first 10 results (Thanks,[@Mike-Nahmias](https://github.com/Mike-Nahmias))
+- Fixed an issue where unhandled 4XX and 5XX HTTP errors would cause a JSONDecodeError
 
 ## [0.12.0] - 2019-04-03
 
diff --git a/canvasapi/requester.py b/canvasapi/requester.py
index 41650a3..1b6e0e1 100644
--- a/canvasapi/requester.py
+++ b/canvasapi/requester.py
@@ -109,8 +109,11 @@ class Requester(object):
             raise ResourceDoesNotExist('Not Found')
         elif response.status_code == 409:
             raise Conflict(response.text)
-        elif response.status_code == 500:
-            raise CanvasException("API encountered an error processing your request")
+        elif response.status_code > 400:
+            # generic catch-all for error codes
+            raise CanvasException(
+                "Encountered an error: status code {}".format(response.status_code)
+            )
 
         return response
 


Test patch:
diff --git a/tests/fixtures/requests.json b/tests/fixtures/requests.json
index 08ee7d1..09bbfd0 100644
--- a/tests/fixtures/requests.json
+++ b/tests/fixtures/requests.json
@@ -38,6 +38,24 @@
 		"data": {},
 		"status_code": 500
 	},
+	"502": {
+		"method": "ANY",
+		"endpoint": "502",
+		"data": {},
+		"status_code": 502
+	},
+	"503": {
+		"method": "ANY",
+		"endpoint": "503",
+		"data": {},
+		"status_code": 503
+	},
+	"absurd": {
+		"method": "ANY",
+		"endpoint": "absurd",
+		"data": {},
+		"status_code": 9001
+	},
 	"delete": {
 		"method": "DELETE",
 		"endpoint": "fake_delete_request",
diff --git a/tests/test_requester.py b/tests/test_requester.py
index e5be5b6..c9adfae 100644
--- a/tests/test_requester.py
+++ b/tests/test_requester.py
@@ -146,3 +146,15 @@ class TestRequester(unittest.TestCase):
 
         with self.assertRaises(CanvasException):
             self.requester.request('GET', '500')
+
+    def test_request_generic(self, m):
+        register_uris({'requests': ['502', '503', 'absurd']}, m)
+
+        with self.assertRaises(CanvasException):
+            self.requester.request('GET', '502')
+
+        with self.assertRaises(CanvasException):
+            self.requester.request('GET', '503')
+
+        with self.assertRaises(CanvasException):
+            self.requester.request('GET', 'absurd')