diff --git a/HISTORY.rst b/HISTORY.rst index 2091ac84..d6b8f496 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,11 @@ History ------- +0.10.4 (2012-02-20) ++++++++++++++++++++ + +* Honor netrc. + 0.10.3 (2012-02-20) +++++++++++++++++++ diff --git a/docs/index.rst b/docs/index.rst index df0f1937..232d6830 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -72,6 +72,7 @@ Requests is ready for today's web. - Unicode Response Bodies - Multipart File Uploads - Connection Timeouts +- ``.netrc`` support User Guide diff --git a/requests/models.py b/requests/models.py index 2c0b2e08..a238214a 100644 --- a/requests/models.py +++ b/requests/models.py @@ -27,7 +27,7 @@ from .exceptions import ( URLRequired, SSLError) from .utils import ( get_encoding_from_headers, stream_untransfer, guess_filename, requote_uri, - dict_from_string, stream_decode_response_unicode) + dict_from_string, stream_decode_response_unicode, get_netrc_auth) from .compat import ( urlparse, urlunparse, urljoin, urlsplit, urlencode, str, bytes, SimpleCookie, is_py2) @@ -435,6 +435,10 @@ class Request(object): if (content_type) and (not 'content-type' in self.headers): self.headers['Content-Type'] = content_type + # Use .netrc auth if none was provided. + if not self.auth: + self.auth = get_netrc_auth(url) + if self.auth: if isinstance(self.auth, tuple) and len(self.auth) == 2: # special-case basic HTTP auth diff --git a/requests/sessions.py b/requests/sessions.py index 7aafd0ea..29ae1d9a 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -40,7 +40,7 @@ def merge_kwargs(local_kwarg, default_kwarg): kwargs.update(local_kwarg) # Remove keys that are set to None. - for (k,v) in list(local_kwarg.items()): + for (k, v) in list(local_kwarg.items()): if v is None: del kwargs[k] diff --git a/requests/utils.py b/requests/utils.py index 97f58600..68efa469 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -11,15 +11,49 @@ that are also useful for external consumption. import cgi import codecs +import os import random import re import zlib +from netrc import netrc, NetrcParseError from .compat import parse_http_list as _parse_list_header -from .compat import quote, cookielib, SimpleCookie, is_py2 +from .compat import quote, cookielib, SimpleCookie, is_py2, urlparse from .compat import basestring, bytes +NETRC_FILES = ('.netrc', '_netrc') + + +def get_netrc_auth(url): + """Returns the Requests tuple auth for a given url from netrc.""" + + locations = (os.path.expanduser('~/{0}'.format(f)) for f in NETRC_FILES) + netrc_path = None + + for loc in locations: + if os.path.exists(loc) and not netrc_path: + netrc_path = loc + + # Abort early if there isn't one. + if netrc_path is None: + return netrc_path + + ri = urlparse(url) + + # Strip port numbers from netloc + host = ri.netloc.split(':')[0] + + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc: + # Return with login / password + login_i = (0 if _netrc[0] else 1) + return (_netrc[login_i], _netrc[2]) + except NetrcParseError: + pass + + def dict_from_string(s): """Returns a MultiDict with Cookies.""" @@ -149,7 +183,7 @@ def header_expand(headers): headers = list(headers.items()) elif isinstance(headers, basestring): return headers - elif isinstance(headers, unicode): + elif isinstance(headers, str): # As discussed in https://github.com/kennethreitz/requests/issues/400 # latin-1 is the most conservative encoding used on the web. Anyone # who needs more can encode to a byte-string before calling