From 08f4249527ea4cb71f120a451aa83780a9e39893 Mon Sep 17 00:00:00 2001 From: SendGrid's DX Team Date: Tue, 4 Apr 2017 13:08:46 -0700 Subject: [PATCH 01/10] Update CLA process --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b7ba36..31d4af0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,13 +16,13 @@ We use [Milestones](https://github.com/sendgrid/python-http-client/milestones) t ## CLAs and CCLAs -Before you get started, SendGrid requires that a SendGrid Contributor License Agreement (CLA) or a SendGrid Company Contributor Licensing Agreement (CCLA) be filled out by every contributor to a SendGrid open source project. +Before you get started, SendGrid requires that a SendGrid Contributor License Agreement (CLA) be filled out by every contributor to a SendGrid open source project. -Our goal with the CLA and CCLA is to clarify the rights of our contributors and reduce other risks arising from inappropriate contributions. The CLA also clarifies the rights SendGrid holds in each contribution and helps to avoid misunderstandings over what rights each contributor is required to grant to SendGrid when making a contribution. In this way the CLA and CCLA encourage broad participation by our open source community and help us build strong open source projects, free from any individual contributor withholding or revoking rights to any contribution. +Our goal with the CLA is to clarify the rights of our contributors and reduce other risks arising from inappropriate contributions. The CLA also clarifies the rights SendGrid holds in each contribution and helps to avoid misunderstandings over what rights each contributor is required to grant to SendGrid when making a contribution. In this way the CLA encourages broad participation by our open source community and helps us build strong open source projects, free from any individual contributor withholding or revoking rights to any contribution. -SendGrid does not merge a pull request made against a SendGrid open source project until that pull request is associated with a signed CLA (or CCLA). Copies of the CLA and CCLA are available [here](https://drive.google.com/a/sendgrid.com/file/d/0B0PlcM9qA91LN2VEUTJWU2RIVXc/view). +SendGrid does not merge a pull request made against a SendGrid open source project until that pull request is associated with a signed CLA. Copies of the CLA are available [here](https://gist.github.com/SendGridDX/98b42c0a5d500058357b80278fde3be8#file-sendgrid_cla). -You may submit your completed [CLA or CCLA](https://drive.google.com/a/sendgrid.com/file/d/0B0PlcM9qA91LN2VEUTJWU2RIVXc/view) to SendGrid at [dx@sendgrid.com](mailto:dx@sendgrid.com). SendGrid will then confirm you are ready to begin making contributions. +When you create a Pull Request, after a few seconds, a comment will appear with a link to the CLA. Click the link and fill out the brief form and then click the "I agree" button and you are all set. You will not be asked to re-sign the CLA unless we make a change. There are a few ways to contribute, which we'll enumerate below: From 86b2f085e1ff09e1085290cd2d342815d775b6f6 Mon Sep 17 00:00:00 2001 From: Dibya Das Date: Wed, 24 May 2017 14:41:16 +0530 Subject: [PATCH 02/10] add support for error handling --- python_http_client/__init__.py | 1 + python_http_client/client.py | 11 ++++-- python_http_client/exceptions.py | 60 ++++++++++++++++++++++++++++++++ tests/test_unit.py | 30 +++++++++++++++- 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 python_http_client/exceptions.py diff --git a/python_http_client/__init__.py b/python_http_client/__init__.py index 3ff722b..d616f8c 100644 --- a/python_http_client/__init__.py +++ b/python_http_client/__init__.py @@ -1 +1,2 @@ from .client import Client +from .exceptions import * \ No newline at end of file diff --git a/python_http_client/client.py b/python_http_client/client.py index dd24c48..83c0423 100644 --- a/python_http_client/client.py +++ b/python_http_client/client.py @@ -1,14 +1,16 @@ """HTTP Client library""" import json - +from .exceptions import handle_error try: # Python 3 import urllib.request as urllib from urllib.parse import urlencode + from urllib.error import HTTPError except ImportError: # Python 2 import urllib2 as urllib + from urllib2 import HTTPError from urllib import urlencode @@ -135,7 +137,12 @@ def _make_request(self, opener, request): :type request: urllib.Request object :return: urllib response """ - return opener.open(request) + try: + return opener.open(request) + except HTTPError as err: + exc = handle_error(err) + exc.__cause__ = None + raise exc def _(self, name): """Add variable values to the url. diff --git a/python_http_client/exceptions.py b/python_http_client/exceptions.py new file mode 100644 index 0000000..4f55e15 --- /dev/null +++ b/python_http_client/exceptions.py @@ -0,0 +1,60 @@ +class HTTPError(Exception): + ''' Base of all other errors''' + def __init__(self,error): + self.code = error.code + self.reason = error.reason + self.body = error.read() + self.headers = error.hdrs + +class BadRequestsError(HTTPError): + pass + +class UnauthorizedError(HTTPError): + pass + +class ForbiddenError(HTTPError): + pass + +class NotFoundError(HTTPError): + pass + +class MethodNotAllowedError(HTTPError): + pass + +class PayloadTooLargeError(HTTPError): + pass + +class UnsupportedMediaTypeError(HTTPError): + pass + +class TooManyRequestsError(HTTPError): + pass + +class InternalServerError(HTTPError): + pass + +class ServiceUnavailableError(HTTPError): + pass + +class GatewayTimeoutError(HTTPError): + pass + +err_dict = { 400 : BadRequestsError, + 401 : UnauthorizedError, + 403 : ForbiddenError, + 404 : NotFoundError, + 405 : MethodNotAllowedError, + 413 : PayloadTooLargeError, + 415 : UnsupportedMediaTypeError, + 429 : TooManyRequestsError, + 500 : InternalServerError, + 503 : ServiceUnavailableError, + 504 : GatewayTimeoutError +} + +def handle_error(error): + try: + exc = err_dict[error.code](error) + except KeyError as e: + return HTTPError(error) + return exc diff --git a/tests/test_unit.py b/tests/test_unit.py index f0c1847..c11975a 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -5,6 +5,7 @@ except ImportError: import unittest from python_http_client.client import Client, Response +from python_http_client.exceptions import handle_error, HTTPError, BadRequestsError, NotFoundError, UnsupportedMediaTypeError, ServiceUnavailableError try: @@ -21,6 +22,15 @@ basestring = str +class MockException(HTTPError): + def __init__(self,code): + self.code = code + self.reason = 'REASON' + self.hdrs = 'HEADERS' + def read(self): + return 'BODY' + + class MockResponse(urllib.HTTPSHandler): def __init__(self, response_code): @@ -43,7 +53,11 @@ def __init__(self, host, response_code): Client.__init__(self, host) def _make_request(self, opener, request): - return MockResponse(self.response_code) + if 200 <= self.response_code <299: # if successsful code + return MockResponse(self.response_code) + else: + raise handle_error(MockException(self.response_code)) + class TestClient(unittest.TestCase): @@ -148,6 +162,20 @@ def test__getattr__(self): r = mock_client.delete() self.assertEqual(r.status_code, 204) + mock_client.response_code = 400 + self.assertRaises(BadRequestsError,mock_client.get) + + mock_client.response_code = 404 + self.assertRaises(NotFoundError,mock_client.post) + + mock_client.response_code = 415 + self.assertRaises(UnsupportedMediaTypeError,mock_client.patch) + + mock_client.response_code = 503 + self.assertRaises(ServiceUnavailableError,mock_client.delete) + + mock_client.response_code = 523 + self.assertRaises(HTTPError,mock_client.delete) if __name__ == '__main__': unittest.main() From dfa222a7c5a2c3b5598ac990847d9e126adbe15f Mon Sep 17 00:00:00 2001 From: Dibya Das Date: Fri, 9 Jun 2017 21:38:12 +0530 Subject: [PATCH 03/10] few fixes --- python_http_client/__init__.py | 16 +++++++++++++++- python_http_client/exceptions.py | 2 +- tests/test_unit.py | 11 +++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/python_http_client/__init__.py b/python_http_client/__init__.py index d616f8c..bceb1af 100644 --- a/python_http_client/__init__.py +++ b/python_http_client/__init__.py @@ -1,2 +1,16 @@ from .client import Client -from .exceptions import * \ No newline at end of file +from .exceptions import ( + HTTPError, + BadRequestsError, + UnauthorizedError, + ForbiddenError, + NotFoundError, + MethodNotAllowedError, + PayloadTooLargeError, + UnsupportedMediaTypeError, + TooManyRequestsError, + InternalServerError, + ServiceUnavailableError, + GatewayTimeoutError +) + diff --git a/python_http_client/exceptions.py b/python_http_client/exceptions.py index 4f55e15..d61d390 100644 --- a/python_http_client/exceptions.py +++ b/python_http_client/exceptions.py @@ -1,7 +1,7 @@ class HTTPError(Exception): ''' Base of all other errors''' def __init__(self,error): - self.code = error.code + self.status_code = error.code self.reason = error.reason self.body = error.read() self.headers = error.hdrs diff --git a/tests/test_unit.py b/tests/test_unit.py index c11975a..00f5b1a 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -5,7 +5,14 @@ except ImportError: import unittest from python_http_client.client import Client, Response -from python_http_client.exceptions import handle_error, HTTPError, BadRequestsError, NotFoundError, UnsupportedMediaTypeError, ServiceUnavailableError +from python_http_client.exceptions import ( + handle_error, + HTTPError, + BadRequestsError, + NotFoundError, + UnsupportedMediaTypeError, + ServiceUnavailableError +) try: @@ -24,7 +31,7 @@ class MockException(HTTPError): def __init__(self,code): - self.code = code + self.status_code = code self.reason = 'REASON' self.hdrs = 'HEADERS' def read(self): From 964c0f8bb537ca9b1792c8a931eeaa7b42b3bf57 Mon Sep 17 00:00:00 2001 From: Dibya Das Date: Fri, 9 Jun 2017 21:41:58 +0530 Subject: [PATCH 04/10] fix --- python_http_client/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_http_client/exceptions.py b/python_http_client/exceptions.py index d61d390..5320c1d 100644 --- a/python_http_client/exceptions.py +++ b/python_http_client/exceptions.py @@ -54,7 +54,7 @@ class GatewayTimeoutError(HTTPError): def handle_error(error): try: - exc = err_dict[error.code](error) + exc = err_dict[error.status_code](error) except KeyError as e: return HTTPError(error) return exc From a89c59ac2ecb440b8ce5e5a42c7f3219f96f06c5 Mon Sep 17 00:00:00 2001 From: Dibya Das Date: Fri, 9 Jun 2017 21:41:58 +0530 Subject: [PATCH 05/10] fix --- python_http_client/exceptions.py | 2 +- tests/test_unit.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python_http_client/exceptions.py b/python_http_client/exceptions.py index 5320c1d..d61d390 100644 --- a/python_http_client/exceptions.py +++ b/python_http_client/exceptions.py @@ -54,7 +54,7 @@ class GatewayTimeoutError(HTTPError): def handle_error(error): try: - exc = err_dict[error.status_code](error) + exc = err_dict[error.code](error) except KeyError as e: return HTTPError(error) return exc diff --git a/tests/test_unit.py b/tests/test_unit.py index 00f5b1a..d5e8956 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -31,7 +31,7 @@ class MockException(HTTPError): def __init__(self,code): - self.status_code = code + self.code = code self.reason = 'REASON' self.hdrs = 'HEADERS' def read(self): From 2a3b17656dafe4ccdfdf2812e3d16b57100beca2 Mon Sep 17 00:00:00 2001 From: Dibya Das Date: Fri, 9 Jun 2017 21:57:55 +0530 Subject: [PATCH 06/10] trigger travis From 5019bf0d48e8c95b3d931256ea47b2ff7469ff23 Mon Sep 17 00:00:00 2001 From: Elmer Thomas Date: Fri, 16 Jun 2017 14:23:37 -0700 Subject: [PATCH 07/10] Updating coveralls init --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e3a7d90..6548ef6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - pip install coveralls script: - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then unit2 discover; else python -m unittest discover; fi -- coverage run tests/test_unit.py +- coverage run --source=python_http_client setup.py tests/test_unit.py notifications: hipchat: rooms: From 76f7d8d46596fb7990e0286e882e462793a322e2 Mon Sep 17 00:00:00 2001 From: Elmer Thomas Date: Fri, 16 Jun 2017 14:27:57 -0700 Subject: [PATCH 08/10] Updating coveralls init --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6548ef6..d9d859b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - pip install coveralls script: - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then unit2 discover; else python -m unittest discover; fi -- coverage run --source=python_http_client setup.py tests/test_unit.py +- coverage run --source=python_http_client tests/test_unit.py notifications: hipchat: rooms: From d40c43d50a21828482c3dde7e3bdb56f7d5845fb Mon Sep 17 00:00:00 2001 From: Elmer Thomas Date: Fri, 16 Jun 2017 14:30:39 -0700 Subject: [PATCH 09/10] Updating coveralls init --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d9d859b..e3a7d90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - pip install coveralls script: - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then unit2 discover; else python -m unittest discover; fi -- coverage run --source=python_http_client tests/test_unit.py +- coverage run tests/test_unit.py notifications: hipchat: rooms: From 2a87bd53589714ef4711c1981b8423121037df91 Mon Sep 17 00:00:00 2001 From: Elmer Thomas Date: Fri, 16 Jun 2017 14:37:06 -0700 Subject: [PATCH 10/10] Remove coveralls --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e3a7d90..bc0d0f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,8 @@ install: - python setup.py install - if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi - if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi -- pip install coveralls script: - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then unit2 discover; else python -m unittest discover; fi -- coverage run tests/test_unit.py notifications: hipchat: rooms: @@ -29,5 +27,3 @@ env: global: - secure: 7j0Ox9ZQss0PewrhGih86Ro0pQJpv23Bb0uABJV7kKN/W37ofrG0m6joamf8EhDDleaPoIqfbARhUpAlqFZF0Uo/5rREqpKkmP4e1zuYMu20VbFv6PXwZ+7ByAuKXN2/Xs54MImlL1+RaduMPNRpbcfT1mdqJgSC+3tVcWodzuRG9RPzxtWYLe93QfwNHV/VMsDPDIY12FZTErbXd/hBCEQXep5rNfK+TtLIGn0ZnS7TktTcD0ld+0ruhunbDjnkpXPVSJDuLaGRpotq0oyaGifnjVM5gVubP+KCL3h24tIXjJ7uI36Eu3EuF4qsg0fmNjuM/WjgwZ9Ta4I2MHlXtFs//qMMArOw5AvPg25adrEwGO4Veh3I3tJGL7hJeM7AZX4rAycXiGIHvpP2G/nX6e/EqRrnFBDOStmBhxEaknLJ/p2Cv6AOvxTMKDo8y+tJY1jp3H1iwCBYyW6KuFKVPDYtu8VLxJunaqNX4LxiJN7VHgvTSgqImjzEy5tVxVt079ciyeznSKKGHLHDAl1ioQpmv/Oyas007A4PKJJAf73go8Yt+GM6qe3K6U3tIBKWL8e0cK1kejk9TLC0D9KXbmhmK81QzpBdQfkrveYi/kucVv0zdrGl+Uy8zcq+vYxceyCdDYcTxCS66bWNFTD2t1dML5gRpdNVVSc27ZM9wtA= - secure: NlSZq/v2vjPQSSjlAbrM1JAfCdBSF/OqmO1HV/7U8HAmyGj7WjAcBkH5qWb5lP/xgUSzP3rEtNBJQNNHHiHHxSY0TtplUkJHrBqZOWGd4nG4GB/w8thj4nOiuok9lQhU2wi4mhRnzw2gGG9XpRpnYqL3a0CWWZ8XilSdL3M1H4fE2rwCSbKo35wpaapAT2BkN/zXeJ62wYX0vsz14EAzRSPlX+zfSo4esjig/B4ubgD1KKq3vRWGX0oU1/b6LYxrRl+OPqql9s3nKa0SuHtzLH4CVM0JTpJ8PxYq/LaLn03evAtgjR3aJJUlXaYL+yVBdATGrtyGUAJTVvRtbWsiaW4KNs+e5eWD+KM1ei18DYHWTMsjRbKLh3DrnUxFSFezMkOgUX4I9aohqPW9q9eTbSi2nR2mEcfDrGPArTZKtmGvx09gil5BAvsYc9A2Ob+TdV0N/bHROdK1R381mY4xWYytZ070+J4YHIKi/AwEJXtYgedc/PDr6fxh9RKDXNybyP2y/i+b72bnij9ZyJc0scDAlRQ4MU/h4cFDohI9quIYpJZ3N3eUeVp7TNX4AT2z+aNj74pBy15eMJv8WYhuBauk3jexhpMQi5yDr7aqlb2/NRyd91oP5QZOcjo7nnPcJp8QyvKtWFeID+c5dV3wcIMeOXmPz1KWWGlJMrV1vZI= -after_success: - coveralls \ No newline at end of file