Skip to content
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
37 changes: 37 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,43 @@
Release History
---------------

2.8.0 (2015-10-05)
++++++++++++++++++

**Minor Improvements** (Backwards Compatible)

- Requests now supports per-host proxies. This allows the ``proxies``
dictionary to have entries of the form
``{'<scheme>://<hostname>': '<proxy>'}``. Host-specific proxies will be used
in preference to the previously-supported scheme-specific ones, but the
previous syntax will continue to work.
- ``Response.raise_for_status`` now prints the URL that failed as part of the
exception message.
- ``requests.utils.get_netrc_auth`` now takes an ``raise_errors`` kwarg,
defaulting to ``False``. When ``True``, errors parsing ``.netrc`` files cause
exceptions to be thrown.
- Change to bundled projects import logic to make it easier to unbundle
requests downstream.
- Changed the default User-Agent string to avoid leaking data on Linux: now
contains only the requests version.

**Bugfixes**

- The ``json`` parameter to ``post()`` and friends will now only be used if
neither ``data`` nor ``files`` are present, consistent with the
documentation.
- We now ignore empty fields in the ``NO_PROXY`` enviroment variable.
- Fixed problem where ``httplib.BadStatusLine`` would get raised if combining
``stream=True`` with ``contextlib.closing``.
- Prevented bugs where we would attempt to return the same connection back to
the connection pool twice when sending a Chunked body.
- Miscellaneous minor internal changes.
- Digest Auth support is now thread safe.

**Updates**

- Updated urllib3 to 1.12.

2.7.0 (2015-05-03)
++++++++++++++++++

Expand Down
4 changes: 2 additions & 2 deletions requests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"""

__title__ = 'requests'
__version__ = '2.7.0'
__build__ = 0x020700
__version__ = '2.8.0'
__build__ = 0x020800
__author__ = 'Kenneth Reitz'
__license__ = 'Apache 2.0'
__copyright__ = 'Copyright 2015 Kenneth Reitz'
Expand Down
2 changes: 1 addition & 1 deletion requests/packages/urllib3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
__license__ = 'MIT'
__version__ = 'dev'
__version__ = '1.12'


from .connectionpool import (
Expand Down
58 changes: 29 additions & 29 deletions requests/packages/urllib3/_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,7 @@ def keys(self):
return list(iterkeys(self._container))


_dict_setitem = dict.__setitem__
_dict_getitem = dict.__getitem__
_dict_delitem = dict.__delitem__
_dict_contains = dict.__contains__
_dict_setdefault = dict.setdefault


class HTTPHeaderDict(dict):
class HTTPHeaderDict(MutableMapping):
"""
:param headers:
An iterable of field-value pairs. Must not contain multiple field names
Expand Down Expand Up @@ -139,7 +132,8 @@ class HTTPHeaderDict(dict):
"""

def __init__(self, headers=None, **kwargs):
dict.__init__(self)
super(HTTPHeaderDict, self).__init__()
self._container = {}
if headers is not None:
if isinstance(headers, HTTPHeaderDict):
self._copy_from(headers)
Expand All @@ -149,38 +143,44 @@ def __init__(self, headers=None, **kwargs):
self.extend(kwargs)

def __setitem__(self, key, val):
return _dict_setitem(self, key.lower(), (key, val))
self._container[key.lower()] = (key, val)
return self._container[key.lower()]

def __getitem__(self, key):
val = _dict_getitem(self, key.lower())
val = self._container[key.lower()]
return ', '.join(val[1:])

def __delitem__(self, key):
return _dict_delitem(self, key.lower())
del self._container[key.lower()]

def __contains__(self, key):
return _dict_contains(self, key.lower())
return key.lower() in self._container

def __eq__(self, other):
if not isinstance(other, Mapping) and not hasattr(other, 'keys'):
return False
if not isinstance(other, type(self)):
other = type(self)(other)
return dict((k1, self[k1]) for k1 in self) == dict((k2, other[k2]) for k2 in other)
return (dict((k.lower(), v) for k, v in self.itermerged()) ==
dict((k.lower(), v) for k, v in other.itermerged()))

def __ne__(self, other):
return not self.__eq__(other)

values = MutableMapping.values
get = MutableMapping.get
update = MutableMapping.update

if not PY3: # Python 2
iterkeys = MutableMapping.iterkeys
itervalues = MutableMapping.itervalues

__marker = object()

def __len__(self):
return len(self._container)

def __iter__(self):
# Only provide the originally cased names
for vals in self._container.values():
yield vals[0]

def pop(self, key, default=__marker):
'''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised.
Expand Down Expand Up @@ -216,7 +216,7 @@ def add(self, key, val):
key_lower = key.lower()
new_vals = key, val
# Keep the common case aka no item present as fast as possible
vals = _dict_setdefault(self, key_lower, new_vals)
vals = self._container.setdefault(key_lower, new_vals)
if new_vals is not vals:
# new_vals was not inserted, as there was a previous one
if isinstance(vals, list):
Expand All @@ -225,7 +225,7 @@ def add(self, key, val):
else:
# vals should be a tuple then, i.e. only one item so far
# Need to convert the tuple to list for further extension
_dict_setitem(self, key_lower, [vals[0], vals[1], val])
self._container[key_lower] = [vals[0], vals[1], val]

def extend(self, *args, **kwargs):
"""Generic import function for any type of header-like object.
Expand All @@ -236,7 +236,7 @@ def extend(self, *args, **kwargs):
raise TypeError("extend() takes at most 1 positional "
"arguments ({} given)".format(len(args)))
other = args[0] if len(args) >= 1 else ()

if isinstance(other, HTTPHeaderDict):
for key, val in other.iteritems():
self.add(key, val)
Expand All @@ -257,7 +257,7 @@ def getlist(self, key):
"""Returns a list of all the values for the named field. Returns an
empty list if the key doesn't exist."""
try:
vals = _dict_getitem(self, key.lower())
vals = self._container[key.lower()]
except KeyError:
return []
else:
Expand All @@ -276,11 +276,11 @@ def __repr__(self):

def _copy_from(self, other):
for key in other:
val = _dict_getitem(other, key)
val = other.getlist(key)
if isinstance(val, list):
# Don't need to convert tuples
val = list(val)
_dict_setitem(self, key, val)
self._container[key.lower()] = [key] + val

def copy(self):
clone = type(self)()
Expand All @@ -290,14 +290,14 @@ def copy(self):
def iteritems(self):
"""Iterate over all header lines, including duplicate ones."""
for key in self:
vals = _dict_getitem(self, key)
vals = self._container[key.lower()]
for val in vals[1:]:
yield vals[0], val

def itermerged(self):
"""Iterate over all headers, merging duplicate ones together."""
for key in self:
val = _dict_getitem(self, key)
val = self._container[key.lower()]
yield val[0], ', '.join(val[1:])

def items(self):
Expand All @@ -307,16 +307,16 @@ def items(self):
def from_httplib(cls, message): # Python 2
"""Read headers from a Python 2 httplib message object."""
# python2.7 does not expose a proper API for exporting multiheaders
# efficiently. This function re-reads raw lines from the message
# efficiently. This function re-reads raw lines from the message
# object and extracts the multiheaders properly.
headers = []

for line in message.headers:
if line.startswith((' ', '\t')):
key, value = headers[-1]
headers[-1] = (key, value + '\r\n' + line.rstrip())
continue

key, value = line.split(':', 1)
headers.append((key, value.strip()))

Expand Down
19 changes: 14 additions & 5 deletions requests/packages/urllib3/connection.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import datetime
import sys
import socket
from socket import timeout as SocketTimeout
from socket import error as SocketError, timeout as SocketTimeout
import warnings
from .packages import six

Expand Down Expand Up @@ -36,9 +36,10 @@ class ConnectionError(Exception):


from .exceptions import (
NewConnectionError,
ConnectTimeoutError,
SystemTimeWarning,
SubjectAltNameWarning,
SystemTimeWarning,
)
from .packages.ssl_match_hostname import match_hostname

Expand Down Expand Up @@ -133,11 +134,15 @@ def _new_conn(self):
conn = connection.create_connection(
(self.host, self.port), self.timeout, **extra_kw)

except SocketTimeout:
except SocketTimeout as e:
raise ConnectTimeoutError(
self, "Connection to %s timed out. (connect timeout=%s)" %
(self.host, self.timeout))

except SocketError as e:
raise NewConnectionError(
self, "Failed to establish a new connection: %s" % e)

return conn

def _prepare_conn(self, conn):
Expand Down Expand Up @@ -185,20 +190,23 @@ class VerifiedHTTPSConnection(HTTPSConnection):
"""
cert_reqs = None
ca_certs = None
ca_cert_dir = None
ssl_version = None
assert_fingerprint = None

def set_cert(self, key_file=None, cert_file=None,
cert_reqs=None, ca_certs=None,
assert_hostname=None, assert_fingerprint=None):
assert_hostname=None, assert_fingerprint=None,
ca_cert_dir=None):

if ca_certs and cert_reqs is None:
if (ca_certs or ca_cert_dir) and cert_reqs is None:
cert_reqs = 'CERT_REQUIRED'

self.key_file = key_file
self.cert_file = cert_file
self.cert_reqs = cert_reqs
self.ca_certs = ca_certs
self.ca_cert_dir = ca_cert_dir
self.assert_hostname = assert_hostname
self.assert_fingerprint = assert_fingerprint

Expand Down Expand Up @@ -237,6 +245,7 @@ def connect(self):
self.sock = ssl_wrap_socket(conn, self.key_file, self.cert_file,
cert_reqs=resolved_cert_reqs,
ca_certs=self.ca_certs,
ca_cert_dir=self.ca_cert_dir,
server_hostname=hostname,
ssl_version=resolved_ssl_version)

Expand Down
20 changes: 12 additions & 8 deletions requests/packages/urllib3/connectionpool.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
LocationValueError,
MaxRetryError,
ProxyError,
ConnectTimeoutError,
ReadTimeoutError,
SSLError,
TimeoutError,
InsecureRequestWarning,
NewConnectionError,
)
from .packages.ssl_match_hostname import CertificateError
from .packages import six
Expand Down Expand Up @@ -422,7 +424,7 @@ def is_same_host(self, url):

# TODO: Add optional support for socket.gethostbyname checking.
scheme, host, port = get_host(url)

# Use explicit default port for comparison when none is given
if self.port and not port:
port = port_by_scheme.get(scheme)
Expand Down Expand Up @@ -592,13 +594,13 @@ def urlopen(self, method, url, body=None, headers=None, retries=None,
release_conn = True
raise

except (TimeoutError, HTTPException, SocketError, ConnectionError) as e:
except (TimeoutError, HTTPException, SocketError, ProtocolError) as e:
# Discard the connection for these exceptions. It will be
# be replaced during the next _get_conn() call.
conn = conn and conn.close()
release_conn = True

if isinstance(e, SocketError) and self.proxy:
if isinstance(e, (SocketError, NewConnectionError)) and self.proxy:
e = ProxyError('Cannot connect to proxy.', e)
elif isinstance(e, (SocketError, HTTPException)):
e = ProtocolError('Connection aborted.', e)
Expand Down Expand Up @@ -675,10 +677,10 @@ class HTTPSConnectionPool(HTTPConnectionPool):
``assert_hostname`` and ``host`` in this order to verify connections.
If ``assert_hostname`` is False, no verification is done.

The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs`` and
``ssl_version`` are only used if :mod:`ssl` is available and are fed into
:meth:`urllib3.util.ssl_wrap_socket` to upgrade the connection socket
into an SSL socket.
The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``,
``ca_cert_dir``, and ``ssl_version`` are only used if :mod:`ssl` is
available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade
the connection socket into an SSL socket.
"""

scheme = 'https'
Expand All @@ -691,7 +693,7 @@ def __init__(self, host, port=None,
key_file=None, cert_file=None, cert_reqs=None,
ca_certs=None, ssl_version=None,
assert_hostname=None, assert_fingerprint=None,
**conn_kw):
ca_cert_dir=None, **conn_kw):

HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize,
block, headers, retries, _proxy, _proxy_headers,
Expand All @@ -704,6 +706,7 @@ def __init__(self, host, port=None,
self.cert_file = cert_file
self.cert_reqs = cert_reqs
self.ca_certs = ca_certs
self.ca_cert_dir = ca_cert_dir
self.ssl_version = ssl_version
self.assert_hostname = assert_hostname
self.assert_fingerprint = assert_fingerprint
Expand All @@ -719,6 +722,7 @@ def _prepare_conn(self, conn):
cert_file=self.cert_file,
cert_reqs=self.cert_reqs,
ca_certs=self.ca_certs,
ca_cert_dir=self.ca_cert_dir,
assert_hostname=self.assert_hostname,
assert_fingerprint=self.assert_fingerprint)
conn.ssl_version = self.ssl_version
Expand Down
Loading