Skip to content

Added support for error handling #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ We use [Milestones](https://github.com/sendgrid/python-http-client/milestones) t
<a name="cla"></a>
## 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:

Expand Down
15 changes: 15 additions & 0 deletions python_http_client/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
from .client import Client
from .exceptions import (
HTTPError,
BadRequestsError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
MethodNotAllowedError,
PayloadTooLargeError,
UnsupportedMediaTypeError,
TooManyRequestsError,
InternalServerError,
ServiceUnavailableError,
GatewayTimeoutError
)

11 changes: 9 additions & 2 deletions python_http_client/client.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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.
Expand Down
60 changes: 60 additions & 0 deletions python_http_client/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class HTTPError(Exception):
''' Base of all other errors'''
def __init__(self,error):
self.status_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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please use spaces and pep8


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
37 changes: 36 additions & 1 deletion tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +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
)


try:
Expand All @@ -21,6 +29,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):
Expand All @@ -43,7 +60,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):
Expand Down Expand Up @@ -148,6 +169,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()