original errors get swallowed due to retries

specifically, hostname nonexistence is being swallowed in my case.
It looks like this is because a retry is always attempted if a conn is None in urlib3 (it appears), then after the second invocation the retry is aborted if tries is too large, and finally a max connection exception is thrown (losing the original exception)

``` .python
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> import requests
>>> r = requests.get('http://doesntexist.foo')
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): doesntexist.foo
WARNING:requests.packages.urllib3.connectionpool:Retrying (0 attempts remain) after connection broken by 'gaierror(8, 'nodename nor servname provided, or not known')': /
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "venv/lib/python2.6/site-packages/requests/api.py", line 78, in get
    return request('get', url, **kwargs)
  File "venv/lib/python2.6/site-packages/requests/api.py", line 65, in request
    prefetch=prefetch
  File "venv/lib/python2.6/site-packages/requests/sessions.py", line 191, in request
    r.send(prefetch=prefetch)
  File "venv/lib/python2.6/site-packages/requests/models.py", line 453, in send
    raise ConnectionError(e)
requests.exceptions.ConnectionError: Max retries exceeded for url: /
>>> 
```


note: the above output was generated testing against the current release version 0.8.2.


Is a `ConnectionError` not specific-enough?


I suppose it would be, but the message I get is about failing retries (even when retry is set to 0). I have no visibility at the api level into the actual cause (name not found). 

On Nov 26, 2011, at 7:31 AM, Kenneth Reitzreply@reply.github.com wrote:

> Is a `ConnectionError` not specific-enough?
> 
> ---
> 
> Reply to this email directly or view it on GitHub:
> https://github.com/kennethreitz/requests/issues/289#issuecomment-2882078


Hmm, I see what you're saying. I guess my perspective is the connection was either the issue or it wasn't ;)

I'll see if I can at least add some more introspection.


I can certainly see your point too. It was certainly a type of connection error. ;)
My creating this ticket was because it was just a surprising departure from how the code used to behave, and seemed an unintentional one..

Didn't it used to return a result object with the 'ok' attribute set to true or false, and an error object possibly present?
I was using something like this with an earlier version (0.7.x maybe?)... A bit ugly but it was functional (provided a means to discern the underlying issue).

``` .python
        resp = requests.get(
            url, headers=req_headers, timeout=2, allow_redirects=False,
            hooks={'pre_request': no_follow_redirect_hook})
        if resp.error:
            logging.debug('response error: %s', resp.error)
            if isinstance(resp.error, requests.exceptions.Timeout):
                raise HTTP504Exception('Timeout')
            elif isinstance(resp.error, urllib2.URLError):
                if hasattr(resp.error, 'reason') and \
                    isinstance(resp.error.reason, socket.gaierror):
                    raise HTTP404Exception('Host name not found')
```

My main use-case for knowing a bit more about the underlying connection error, is a proxy service scenario. Knowing the underlying problem can give insight into a proper http response code to the requesting client, as well as a friendly and hopefully helpful error message.

I can certainly work around it and return a more generic message to the requesting clients, but I was wondering if this was an intended change to error handling or an unforeseen one due to the urllib3 code. 

Thanks.


Yes, it was absolutely an intended change — mostly due to the multitude of requests that were made by the community.

There's a `safe_mode` parameter that will surpress the errors, as before.

Here's the docs on the new exceptions: http://docs.python-requests.org/en/latest/user/quickstart/#errors-and-exceptions