Skip to content
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

Added support for error handling #17

Merged
merged 10 commits into from Jun 21, 2017

Conversation

dibyadas
Copy link
Contributor

@dibyadas dibyadas commented May 24, 2017

This PR deals with issue #16

Custom exceptions for python_http_client have been added. Only few exceptions have been added.
Others will be added soon.

@thinkingserious thinkingserious added the status: code review request requesting a community code review or review from Twilio label May 24, 2017
@SendGridDX
Copy link

SendGridDX commented May 24, 2017

CLA assistant check
All committers have signed the CLA.

@dibyadas
Copy link
Contributor Author

Hi @thinkingserious ! I have added custom exception handling. Do suggest changes for improvements.
(P.S - Sorry for the delay. I got caught up in some work)

@dibyadas
Copy link
Contributor Author

Hi @w- ! Have a look at this. What do you think? :)

@w-
Copy link

w- commented May 24, 2017

Hey guys, i'll be out of town on vacation for the next few days. won't be able to respond on this topic till then.

@thinkingserious
Copy link
Contributor

Thanks for the heads up @w- and I hope you enjoy the vacation :)

@thinkingserious
Copy link
Contributor

@w-,

Do you have time to take a look? Thanks!

@dibyadas
Copy link
Contributor Author

dibyadas commented Jun 2, 2017

@thinkingserious,

In the meantime, how about I add tests for this?
Where do you think would be a good place to add?
Should I add it in the existing one or a different file for testing exceptions?

With Regards,
Dibya Prakash Das

@thinkingserious
Copy link
Contributor

@dibyadas,

Please add the test in the existing file. Thanks!

@@ -1 +1,2 @@
from .client import Client
from .exceptions import *
Copy link

Choose a reason for hiding this comment

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

even though there are many classes., I would change this to an explicit import

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed.

503 : ServiceUnavailableError
}

def handle_error(error):
Copy link

@w- w- Jun 6, 2017

Choose a reason for hiding this comment

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

this should probably get_HTTPError(error) and it should return an instance of the specific error class instead of raising it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Umm.. Can you give an example? I can't visualize this.

Copy link

Choose a reason for hiding this comment

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

this is what i think the function should be

def get_HTTPError(error):
	try:
		http_err_obj = err_dict[error.code](error)
	except KeyError:
		http_err_obj = HttpError(error)		
	return http_err_obj

}

def handle_error(error):
exc = err_dict[error.code](error)
Copy link

Choose a reason for hiding this comment

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

we want a try/catch here in event that the error.code doesn't match anything in our dict. it should then set err as a generic HTTPError

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually I just put in a few errors, the ones that SendGrid servers returns, just for showing the approach. If approved, I will add the rest of the error.

Copy link
Contributor

Choose a reason for hiding this comment

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

@dibyadas,

Thanks, that would be awesome. I also agree that we should make sure to try/catch for errors we don't expect as @w- illustrated.

Copy link

@w- w- left a comment

Choose a reason for hiding this comment

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

I was pressing the wrong button earlier so my comments are split up.
Good work @dibyadas


def handle_error(error):
exc = err_dict[error.code](error)
exc.__cause__ = None
Copy link

@w- w- Jun 6, 2017

Choose a reason for hiding this comment

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

as per https://www.python.org/dev/peps/pep-3134/
we probably want __cause__ to be the original error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we won't to do this then an ugly traceback is produced in Python 3. In this PR, we are raising our custom exception when an urllib.HTTPError is raised. Here, in python 3 , such a thing will be displayed the output.

Traceback (most recent call last):
  File "/home/dibya/workspace/python-http-client/python_http_client/client.py", line 141, in _make_request
    return opener.open(request)
  File "/usr/lib/python3.5/urllib/request.py", line 472, in open
    response = meth(req, response)
  File "/usr/lib/python3.5/urllib/request.py", line 582, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python3.5/urllib/request.py", line 504, in error
    result = self._call_chain(*args)
  File "/usr/lib/python3.5/urllib/request.py", line 444, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.5/urllib/request.py", line 696, in http_error_302
    return self.parent.open(new, timeout=req.timeout)
  File "/usr/lib/python3.5/urllib/request.py", line 472, in open
    response = meth(req, response)
  File "/usr/lib/python3.5/urllib/request.py", line 582, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python3.5/urllib/request.py", line 510, in error
    return self._call_chain(*args)
  File "/usr/lib/python3.5/urllib/request.py", line 444, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.5/urllib/request.py", line 590, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 401: UNAUTHORIZED

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    r = a.name.api.get()
  File "/home/dibya/workspace/python-http-client/python_http_client/client.py", line 209, in http_request
    return Response(self._make_request(opener, request))
  File "/home/dibya/workspace/python-http-client/python_http_client/client.py", line 143, in _make_request
    handle_error(err)
  File "/home/dibya/workspace/python-http-client/python_http_client/exceptions.py", line 53, in handle_error
    raise exc
python_http_client.exceptions.UnauthorizedError: HTTP Error 401: UNAUTHORIZED

If we set the __cause__ to None, then only the part below During handling of the above exception, another exception occurred: is shown, which is all that should be displayed.

Copy link

Choose a reason for hiding this comment

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

I see. ok. I guess it is preferable to be None

def __init__(self,error):
self.code = error.code
self.reason = error.reason
#self.headers = error.headers
Copy link

Choose a reason for hiding this comment

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

both headers and body are important to record in the new HTTPError object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The HTTPError thrown by urllib module doesn't have a body attribute and in python versions <= 3.3, they don't have the headers attribute as well. I just commented this out so that this can be discussed further.

Copy link

Choose a reason for hiding this comment

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

it does. you have to read docs and look at the source.

HTTPError is based on URLError which has the following attributes

err.url
err.code
err.hdrs
# to get the body
err.read()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah! Since it was not mentioned in the documentation, I missed it. Thanks for the tip! :)

class HTTPError(Exception):
''' Base of all other errors'''
def __init__(self,error):
self.code = error.code
Copy link

@w- w- Jun 6, 2017

Choose a reason for hiding this comment

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

keeping in line with other http libraries, I think that self.code should actually be self.status_code. personal preference only.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure we can. It'll be more evident.

try:
return opener.open(request)
except HTTPError as err:
handle_error(err)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@w- Here, I am handling the urllib.HTTPError. What you are saying is, we do this:-

try:
    return opener.open(request)
except HTTPError as err:
    raise get_HTTPError(err)

Am I right?

Copy link

Choose a reason for hiding this comment

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

correct.

@thinkingserious
Copy link
Contributor

Nice work @dibyadas and @w-! Looks like we are almost there :)

@dibyadas
Copy link
Contributor Author

dibyadas commented Jun 9, 2017

Hi @thinkingserious and @w- ,
I am not that familiar with adding tests, but I have tried to add some. If have also tried addressing your review comments. Please have a look and suggest changes if any.

Edit :- After pushing an commit the coverage passed but is now failing 😕 I not really familiar with this. I need some help here.

With Best Regards,
Dibya Prakash Das

@dibyadas
Copy link
Contributor Author

Hi @thinkingserious,
I thought it would be good, if you could take a look at this. I am kind of stuck here. The code coverage passed and then failed for reasons I do not know. I thought maybe you could help me with this.

With Regards,
Dibya Prakash Das

@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@sendgrid sendgrid deleted a comment from coveralls Jun 16, 2017
@thinkingserious
Copy link
Contributor

This looks good to me @dibyadas, what do you think @w-?

@w-
Copy link

w- commented Jun 19, 2017

I'm ok with this.

@thinkingserious thinkingserious merged commit 1c71517 into sendgrid:master Jun 21, 2017
@thinkingserious
Copy link
Contributor

Hello @dibyadas,

Thanks again for the PR!

We want to show our appreciation by sending you some swag. Could you please fill out this form so we can send it to you? Thanks!

Team SendGrid DX

@dibyadas
Copy link
Contributor Author

Hi @thinkingserious,
I have filled up the from. Thanks a lot!
With Regards,
Dibya Prakash Das

@iandouglas
Copy link
Contributor

Per https://github.com/sendgrid/sendgrid-python/issues/323 if we could get examples of these awesome new exceptions in the README so other devs don't need to go surfing into tests to see examples of exceptions and what can cause them, I think that'd be super helpful.

Copy link
Contributor

@andriisoldatenko andriisoldatenko left a comment

Choose a reason for hiding this comment

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

Please check code using flake8/pep8

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: code review request requesting a community code review or review from Twilio
Projects
None yet
6 participants