Repository: ucfopen/canvasapi

Issue:
PATCH method should be added
PATCH method should be added to the list of supported request methods. This should be done in the file canvasapi/requester.py, line 176, by adding:
```
      elif method == 'PATCH':
            req_method = self._patch_request
```

In addition, at the end of the same file, add the  following lines:
```   
 def _patch_request(self, url, headers, data=None):
        """
        Issue a PATCH request to the specified endpoint with the data provided.

        :param url: str
        :pararm headers: dict
        :param data: dict
        """
        return self._session.patch(url, headers=headers, data=data)
```

The PATCH method is useful when e.g., patching a late policy.

Discussion:

Patch:
diff --git a/AUTHORS.md b/AUTHORS.md
index ed65e3a..d78bcb2 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -50,6 +50,7 @@ Patches and Suggestions
 - [@Tobiaqs](https://github.com/Tobiaqs)
 - Tuan Pham [@tuanvpham](https://github.com/tuanvpham)
 - Tyler Wallace [@wallacetyler](https://github.com/wallacetyler)
+- [@us91](https://github.com/us91)
 - Weining Li [@weining-li](https://github.com/weining-li)
 - William Funk [@WilliamRADFunk](https://github.com/WilliamRADFunk)
 - William Wesley Monroe [@purpleHey](https://github.com/purpleHey)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 497c655..b653134 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 - 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
+- Added support for PATCH methods (Thanks, [@us91](https://github.com/us91))
 
 ### Deprecation Warnings
 
diff --git a/canvasapi/requester.py b/canvasapi/requester.py
index 1b6e0e1..9e19343 100644
--- a/canvasapi/requester.py
+++ b/canvasapi/requester.py
@@ -4,8 +4,13 @@ from datetime import datetime
 import requests
 
 from canvasapi.exceptions import (
-    BadRequest, CanvasException, Conflict, Forbidden, InvalidAccessToken,
-    ResourceDoesNotExist, Unauthorized
+    BadRequest,
+    CanvasException,
+    Conflict,
+    Forbidden,
+    InvalidAccessToken,
+    ResourceDoesNotExist,
+    Unauthorized,
 )
 
 
@@ -27,8 +32,15 @@ class Requester(object):
         self._cache = []
 
     def request(
-            self, method, endpoint=None, headers=None, use_auth=True,
-            _url=None, _kwargs=None, **kwargs):
+        self,
+        method,
+        endpoint=None,
+        headers=None,
+        use_auth=True,
+        _url=None,
+        _kwargs=None,
+        **kwargs
+    ):
         """
         Make a request to the Canvas API and return the response.
 
@@ -85,6 +97,8 @@ class Requester(object):
             req_method = self._delete_request
         elif method == 'PUT':
             req_method = self._put_request
+        elif method == 'PATCH':
+            req_method = self._patch_request
 
         # Call the request method
         response = req_method(full_url, headers, _kwargs)
@@ -161,6 +175,16 @@ class Requester(object):
         """
         return self._session.delete(url, headers=headers, data=data)
 
+    def _patch_request(self, url, headers, data=None):
+        """
+        Issue a PATCH request to the specified endpoint with the data provided.
+
+        :param url: str
+        :pararm headers: dict
+        :param data: dict
+        """
+        return self._session.patch(url, headers=headers, data=data)
+
     def _put_request(self, url, headers, data=None):
         """
         Issue a PUT request to the specified endpoint with the data provided.


Test patch:
diff --git a/tests/fixtures/requests.json b/tests/fixtures/requests.json
index 09bbfd0..5d00c69 100644
--- a/tests/fixtures/requests.json
+++ b/tests/fixtures/requests.json
@@ -74,6 +74,12 @@
 		"data": {},
 		"status_code": 200
 	},
+	"patch": {
+		"method": "PATCH",
+		"endpoint": "fake_patch_request",
+		"data": {},
+		"status_code": 200
+	},
 	"put": {
 		"method": "PUT",
 		"endpoint": "fake_put_request",
diff --git a/tests/test_requester.py b/tests/test_requester.py
index c9adfae..a1b3a4a 100644
--- a/tests/test_requester.py
+++ b/tests/test_requester.py
@@ -8,8 +8,12 @@ from six.moves.urllib.parse import quote
 
 from canvasapi import Canvas
 from canvasapi.exceptions import (
-    BadRequest, CanvasException, Conflict, InvalidAccessToken, ResourceDoesNotExist,
-    Unauthorized
+    BadRequest,
+    CanvasException,
+    Conflict,
+    InvalidAccessToken,
+    ResourceDoesNotExist,
+    Unauthorized,
 )
 from tests import settings
 from tests.util import register_uris
@@ -17,7 +21,6 @@ from tests.util import register_uris
 
 @requests_mock.Mocker()
 class TestRequester(unittest.TestCase):
-
     def setUp(self):
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
         self.requester = self.canvas._Canvas__requester
@@ -71,6 +74,12 @@ class TestRequester(unittest.TestCase):
         response = self.requester.request('DELETE', 'fake_delete_request')
         self.assertEqual(response.status_code, 200)
 
+    def test_request_patch(self, m):
+        register_uris({'requests': ['patch']}, m)
+
+        response = self.requester.request('PATCH', 'fake_patch_request')
+        self.assertEqual(response.status_code, 200)
+
     def test_request_put(self, m):
         register_uris({'requests': ['put']}, m)
 
@@ -103,12 +112,7 @@ class TestRequester(unittest.TestCase):
 
         m.add_matcher(custom_matcher)
 
-        response = self.requester.request(
-            'POST',
-            'test',
-            test=True,
-            test2=False
-        )
+        response = self.requester.request('POST', 'test', test=True, test2=False)
         self.assertEqual(response.status_code, 200)
 
     def test_request_400(self, m):