Document what exceptions may be raised

I'd like to be able to write something which will catch any exceptions raised when performing a request, so I can handle them appropriately.  However, I'm not sure which exceptions might get raised; looking at the code, I think urllib{,2,3} exceptions can occur, as well as ones from requests.exceptions.

Ideally, I'd like to be able to do try:... except requests.exceptions.RequestException and have that catch any exceptions due to making the HTTP request; this could probably be accomplished by monkeypatching the urllib exceptions to add RequestException as a base class, though maybe that's too ugly. Some documentation of what I need to catch, though, would be a good start.


:+1:


@bitprophet has some further thoughts on connection issues that I may or may not agree with.


Yea, thoughts like "man, that @kennethreitz is such a jerk!". ;)

---

A weak Monday morning attempt to recapture my IRC arguments:

There are, IMO, two types of failure modes in this kind of library: network failures, such that you never connect to or hear back from the HTTP server; and HTTP failures, such that you successfully get back a non-200 response.

Kenneth's default is to suppress exceptions, return a valid Request object, and require the user to explicitly ask about exceptions via `Request.raise_for_<whatever>`. (IIRC he does this for both types of failures, but had a thought to create a 2nd `Request.raise_*` method so users could tell the failure types apart.)

My opinion is that this is un-Pythonic and surprising; somebody new to the library but not new to Python will be confused upon getting a successful-but-empty `Request` object, even if the server was down/DNS was down/bad URL/etc.

Furthermore, one will get a "farther down the road" traceback (e.g. `None has no attribute 'blah'` or `IndexError`s or etc etc) when trying to use the `Request` object as if it had succeeded, e.g. using its `.content` or `.raw` attributes, which is more confusing and harder to debug than an obvious, at-connection-time `NetworkError` or whatnot.

Such error suppression needs to have a serious, obvious benefit to offset the negative effects of forcing them to appear in strange ways farther down the stack. I don't see such a benefit here, other than Kenneth's noted use case of making N requests in a row/simultaneously, and not wanting connection errors to abort the entire run.

In that situation, my opinion is one should use an opt-in, "please suppress errors, I will check for them myself" setting, and that it should not be the default behavior.


I'm leaning more towards this myself.


Interesting.

FWIW, I certainly expected exceptions to be raised automatically; didn't even think to look for raise_for_\* methods!  +1 to @bitprophet's suggestion of making this behaviour opt-in.

Making it easy to catch all possible exceptions raised by Requests (without doing an "except:" thing) would be helpful, though.  Either by some fiddling to rebase the exceptions (so I can do "except Requests.Error:") or by some nicer technique if you can come up with one...


I never did really finish my thoughts; I should point out that most of the above relates to the 1st category of errors, i.e. "did not receive any HTTP response due to network troubles."

When actually receiving a non-200 response, or when one successfully connects but gets some sort of garbage back from the server (though that's kind of a gray area) I think the question of whether to raise or depend on the user to check, is not as clear cut.

I still think raising an exception would not be a _bad_ thing (again with the "fail fast" philosophy) but I could see the argument for telling the user "you got some response; but you need to make sure it's the right kind."

_~fin~_


To be clear: this discussion is limited only to network and protocol exceptions.


I'm new to the Python scene, but I agree with @bitprophet. Requests that have no chance of bringing back any meaningful data should fail fast and fail loudly, especially when it comes to malformed URLs.