diff --git a/.travis.yml b/.travis.yml index 38b0ac69bc..ff2a839d98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,6 @@ env: # https://github.com/travis-ci/travis-ci/issues/4794 matrix: include: - - python: 2.6 - env: TOXENV=py26 - python: 2.7 env: TOXENV=py27 - python: 3.4 @@ -37,9 +35,6 @@ matrix: env: TOXENV=py37 - python: pypy-5.4 env: TOXENV=pypy - - language: generic - os: osx - env: TOXENV=py26 - language: generic os: osx env: TOXENV=py27 diff --git a/_travis/install.sh b/_travis/install.sh index dac2204939..76bd226b19 100755 --- a/_travis/install.sh +++ b/_travis/install.sh @@ -17,18 +17,10 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then eval "$(pyenv init -)" case "${TOXENV}" in - py26) - pyenv install 2.6.9 - pyenv global 2.6.9 - ;; py27) pyenv install 2.7.14 pyenv global 2.7.14 ;; - py33) - pyenv install 3.3.6 - pyenv global 3.3.6 - ;; py34) pyenv install 3.4.7 pyenv global 3.4.7 diff --git a/dev-requirements.txt b/dev-requirements.txt index 356b84d4d7..9a2a962a6e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,12 +3,9 @@ coverage==4.5.1 tox==2.9.1 twine==1.11.0 wheel==0.30.0 -tornado==5.0.2; python_version>"2.6" -tornado==4.2.1; python_version<="2.6" +tornado==5.0.2 PySocks==1.6.8 pkginfo==1.4.2 -pytest-timeout==1.3.1; python_version>"2.6" -pytest-timeout==1.2.0; python_version<="2.6" -pytest==3.6.4; python_version>"2.6" -pytest==3.2.5; python_version<="2.6" -gcp-devrel-py-tools==0.0.15; python_version>"2.6" +pytest-timeout==1.3.1 +pytest==3.6.4 +gcp-devrel-py-tools==0.0.15 \ No newline at end of file diff --git a/docs/contributing.rst b/docs/contributing.rst index 206f640fac..13ea6c8b95 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -55,11 +55,12 @@ suite:: $ make test-all [... tox creates a virtualenv for every platform and runs tests inside of each] - py26: commands succeeded py27: commands succeeded - py32: commands succeeded - py33: commands succeeded py34: commands succeeded + py35: commands succeeded + py36: commands succeeded + py37: commands succeeded + pypy: commands succeeded Our test suite `runs continuously on Travis CI `_ with every pull request. diff --git a/dummyserver/testcase.py b/dummyserver/testcase.py index d9ff8cf4a6..ebd0bcb841 100644 --- a/dummyserver/testcase.py +++ b/dummyserver/testcase.py @@ -1,5 +1,5 @@ -import sys import threading +import unittest import pytest from tornado import ioloop, web @@ -14,11 +14,6 @@ from dummyserver.handlers import TestingApp from dummyserver.proxy import ProxyHandler -if sys.version_info >= (2, 7): - import unittest -else: - import unittest2 as unittest - def consume_socket(sock, chunks=65536): while not sock.recv(chunks).endswith(b'\r\n\r\n'): diff --git a/setup.cfg b/setup.cfg index 91c59cb6b3..efb26b8639 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,10 +11,9 @@ provides-extra = secure socks requires-dist = - pyOpenSSL>=0.14,<18.0.0; python_version=="2.6" and extra == 'secure' pyOpenSSL>=0.14; python_version=="2.7" and extra == 'secure' - cryptography>=1.3.4; python_version<="2.7" and extra == 'secure' - idna>=2.0.0; python_version<="2.7" and extra == 'secure' + cryptography>=1.3.4; python_version=="2.7" and extra == 'secure' + idna>=2.0.0; python_version=="2.7" and extra == 'secure' certifi; extra == 'secure' - ipaddress; python_version<="2.7" and extra == 'secure' + ipaddress; python_version=="2.7" and extra == 'secure' PySocks>=1.5.6,<2.0,!=1.5.7; extra == 'socks' diff --git a/setup.py b/setup.py index 17f3377682..28441648b3 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,6 @@ from setuptools import setup import os -import sys import re import codecs @@ -20,12 +19,6 @@ changes = fp.read() version = VERSION -# pyOpenSSL version 18.0.0 dropped support for Python 2.6 -if sys.version_info < (2, 7): - PYOPENSSL_VERSION = 'pyOpenSSL >= 0.14, < 18.0.0' -else: - PYOPENSSL_VERSION = 'pyOpenSSL >= 0.14' - setup(name='urllib3', version=version, description="HTTP library with thread-safe connection pooling, file post, and more.", @@ -37,7 +30,6 @@ 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', @@ -60,7 +52,7 @@ ], package_dir={'': 'src'}, requires=[], - python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4", + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4", tests_require=[ # These are a less-specific subset of dev-requirements.txt, for the # convenience of distro package maintainers. @@ -71,7 +63,7 @@ test_suite='test', extras_require={ 'secure': [ - PYOPENSSL_VERSION, + 'pyOpenSSL >= 0.14', 'cryptography>=1.3.4', 'idna>=2.0.0', 'certifi', diff --git a/src/urllib3/__init__.py b/src/urllib3/__init__.py index 0cd5e34288..f010fc4ed4 100644 --- a/src/urllib3/__init__.py +++ b/src/urllib3/__init__.py @@ -22,12 +22,7 @@ # Set default logging handler to avoid "No handler found" warnings. import logging -try: # Python 2.7+ - from logging import NullHandler -except ImportError: - class NullHandler(logging.Handler): - def emit(self, record): - pass +from logging import NullHandler __author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' __license__ = 'MIT' diff --git a/src/urllib3/_collections.py b/src/urllib3/_collections.py index 6e36b84e59..34f23811c6 100644 --- a/src/urllib3/_collections.py +++ b/src/urllib3/_collections.py @@ -14,10 +14,7 @@ def __exit__(self, exc_type, exc_value, traceback): pass -try: # Python 2.7+ - from collections import OrderedDict -except ImportError: - from .packages.ordered_dict import OrderedDict +from collections import OrderedDict from .exceptions import InvalidHeader from .packages.six import iterkeys, itervalues, PY3 diff --git a/src/urllib3/connection.py b/src/urllib3/connection.py index 4f662b0d68..7c9a9f3b93 100644 --- a/src/urllib3/connection.py +++ b/src/urllib3/connection.py @@ -2,7 +2,6 @@ import datetime import logging import os -import sys import socket from socket import error as SocketError, timeout as SocketTimeout import warnings @@ -78,9 +77,6 @@ class HTTPConnection(_HTTPConnection, object): - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` - ``source_address``: Set the source address for the current connection. - - .. note:: This is ignored for Python 2.6. It is only applied for 2.7 and 3.x - - ``socket_options``: Set specific options on the underlying socket. If not specified, then defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. @@ -108,21 +104,13 @@ def __init__(self, *args, **kw): if six.PY3: # Python 3 kw.pop('strict', None) - # Pre-set source_address in case we have an older Python like 2.6. + # Pre-set source_address. self.source_address = kw.get('source_address') - if sys.version_info < (2, 7): # Python 2.6 - # _HTTPConnection on Python 2.6 will balk at this keyword arg, but - # not newer versions. We can still use it when creating a - # connection though, so we pop it *after* we have saved it as - # self.source_address. - kw.pop('source_address', None) - #: The socket options provided by the user. If no options are #: provided, we use the default options. self.socket_options = kw.pop('socket_options', self.default_socket_options) - # Superclass also sets self.source_address in Python 2.7+. _HTTPConnection.__init__(self, *args, **kw) @property diff --git a/src/urllib3/connectionpool.py b/src/urllib3/connectionpool.py index 8fcb0bce79..8e58fa743f 100644 --- a/src/urllib3/connectionpool.py +++ b/src/urllib3/connectionpool.py @@ -89,7 +89,7 @@ def close(self): # This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 -_blocking_errnos = set([errno.EAGAIN, errno.EWOULDBLOCK]) +_blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK} class HTTPConnectionPool(ConnectionPool, RequestMethods): @@ -375,7 +375,7 @@ def _make_request(self, conn, method, url, timeout=_Default, chunked=False, try: try: # Python 2.7, use buffering of HTTP responses httplib_response = conn.getresponse(buffering=True) - except TypeError: # Python 2.6 and older, Python 3 + except TypeError: # Python 3 try: httplib_response = conn.getresponse() except Exception as e: @@ -801,17 +801,7 @@ def _prepare_proxy(self, conn): Establish tunnel connection early, because otherwise httplib would improperly set Host: header to proxy's IP:port. """ - # Python 2.7+ - try: - set_tunnel = conn.set_tunnel - except AttributeError: # Platform-specific: Python 2.6 - set_tunnel = conn._set_tunnel - - if sys.version_info <= (2, 6, 4) and not self.proxy_headers: # Python 2.6.4 and older - set_tunnel(self._proxy_host, self.port) - else: - set_tunnel(self._proxy_host, self.port, self.proxy_headers) - + conn.set_tunnel(self._proxy_host, self.port, self.proxy_headers) conn.connect() def _new_conn(self): diff --git a/src/urllib3/contrib/ntlmpool.py b/src/urllib3/contrib/ntlmpool.py index 642e99ed2d..8ea127c583 100644 --- a/src/urllib3/contrib/ntlmpool.py +++ b/src/urllib3/contrib/ntlmpool.py @@ -43,8 +43,7 @@ def _new_conn(self): log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s', self.num_connections, self.host, self.authurl) - headers = {} - headers['Connection'] = 'Keep-Alive' + headers = {'Connection': 'Keep-Alive'} req_header = 'Authorization' resp_header = 'www-authenticate' diff --git a/src/urllib3/packages/backports/makefile.py b/src/urllib3/packages/backports/makefile.py index 75b80dcf84..740db377d9 100644 --- a/src/urllib3/packages/backports/makefile.py +++ b/src/urllib3/packages/backports/makefile.py @@ -16,7 +16,7 @@ def backport_makefile(self, mode="r", buffering=None, encoding=None, """ Backport of ``socket.makefile`` from Python 3.5. """ - if not set(mode) <= set(["r", "w", "b"]): + if not set(mode) <= {"r", "w", "b"}: raise ValueError( "invalid mode %r (only r, w, b allowed)" % (mode,) ) diff --git a/src/urllib3/packages/ordered_dict.py b/src/urllib3/packages/ordered_dict.py deleted file mode 100644 index 4479363cc4..0000000000 --- a/src/urllib3/packages/ordered_dict.py +++ /dev/null @@ -1,259 +0,0 @@ -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. -# Copyright 2009 Raymond Hettinger, released under the MIT License. -# http://code.activestate.com/recipes/576693/ -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - pass - - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - - ''' - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.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. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) diff --git a/src/urllib3/request.py b/src/urllib3/request.py index 1be3334113..8f2f44bb21 100644 --- a/src/urllib3/request.py +++ b/src/urllib3/request.py @@ -36,7 +36,7 @@ class RequestMethods(object): explicitly. """ - _encode_url_methods = set(['DELETE', 'GET', 'HEAD', 'OPTIONS']) + _encode_url_methods = {'DELETE', 'GET', 'HEAD', 'OPTIONS'} def __init__(self, headers=None): self.headers = headers or {} diff --git a/src/urllib3/util/ssl_.py b/src/urllib3/util/ssl_.py index 2893752a3d..9ca08bf664 100644 --- a/src/urllib3/util/ssl_.py +++ b/src/urllib3/util/ssl_.py @@ -56,9 +56,8 @@ def _const_compare_digest_backport(a, b): OP_NO_COMPRESSION = 0x20000 -# Python 2.7 and earlier didn't have inet_pton on non-Linux -# so we fallback on inet_aton in those cases. This means that -# we can only detect IPv4 addresses in this case. +# Python 2.7 doesn't have inet_pton on non-Linux so we fallback on inet_aton in +# those cases. This means that we can only detect IPv4 addresses in this case. if hasattr(socket, 'inet_pton'): inet_pton = socket.inet_pton else: @@ -115,9 +114,8 @@ def inet_pton(_, host): except ImportError: import sys - class SSLContext(object): # Platform-specific: Python 2 & 3.1 - supports_set_ciphers = ((2, 7) <= sys.version_info < (3,) or - (3, 2) <= sys.version_info) + class SSLContext(object): # Platform-specific: Python 2 + supports_set_ciphers = True def __init__(self, protocol_version): self.protocol = protocol_version @@ -141,12 +139,6 @@ def load_verify_locations(self, cafile=None, capath=None): raise SSLError("CA directories not supported in older Pythons") def set_ciphers(self, cipher_suite): - if not self.supports_set_ciphers: - raise TypeError( - 'Your version of Python does not support setting ' - 'a custom cipher suite. Please upgrade to Python ' - '2.7, 3.2, or later if you need this functionality.' - ) self.ciphers = cipher_suite def wrap_socket(self, socket, server_hostname=None, server_side=False): @@ -167,10 +159,7 @@ def wrap_socket(self, socket, server_hostname=None, server_side=False): 'ssl_version': self.protocol, 'server_side': server_side, } - if self.supports_set_ciphers: # Platform-specific: Python 2.7+ - return wrap_socket(socket, ciphers=self.ciphers, **kwargs) - else: # Platform-specific: Python 2.6 - return wrap_socket(socket, **kwargs) + return wrap_socket(socket, ciphers=self.ciphers, **kwargs) def assert_fingerprint(cert, fingerprint): @@ -291,9 +280,6 @@ def create_urllib3_context(ssl_version=None, cert_reqs=None, context.options |= options - if getattr(context, 'supports_set_ciphers', True): # Platform-specific: Python 2.6 - context.set_ciphers(ciphers or DEFAULT_CIPHERS) - context.verify_mode = cert_reqs if getattr(context, 'check_hostname', None) is not None: # Platform-specific: Python 3.2 # We do our own verification, including fingerprints and alternative @@ -316,8 +302,7 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, A pre-made :class:`SSLContext` object. If none is provided, one will be created using :func:`create_urllib3_context`. :param ciphers: - A string of ciphers we wish the client to support. This is not - supported on Python 2.6 as the ssl module does not support it. + A string of ciphers we wish the client to support. :param ca_cert_dir: A directory containing CA certificates in multiple separate files, as supported by OpenSSL's -CApath flag or the capath argument to @@ -334,7 +319,7 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, if ca_certs or ca_cert_dir: try: context.load_verify_locations(ca_certs, ca_cert_dir) - except IOError as e: # Platform-specific: Python 2.6, 2.7, 3.2 + except IOError as e: # Platform-specific: Python 2.7 raise SSLError(e) # Py33 raises FileNotFoundError which subclasses OSError # These are not equivalent unless we check the errno attribute diff --git a/test/__init__.py b/test/__init__.py index 39eddd79c6..1db7cd5985 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -4,7 +4,6 @@ import functools import logging import socket -import platform import pytest @@ -37,30 +36,6 @@ def setUp(): warnings.simplefilter('ignore', HTTPWarning) -def onlyPy26OrOlder(test): - """Skips this test unless you are on Python2.6.x or earlier.""" - - @functools.wraps(test) - def wrapper(*args, **kwargs): - msg = "{name} only runs on Python2.6.x or older".format(name=test.__name__) - if sys.version_info >= (2, 7): - pytest.skip(msg) - return test(*args, **kwargs) - return wrapper - - -def onlyPy27OrNewer(test): - """Skips this test unless you are on Python 2.7.x or later.""" - - @functools.wraps(test) - def wrapper(*args, **kwargs): - msg = "{name} requires Python 2.7.x+ to run".format(name=test.__name__) - if sys.version_info < (2, 7): - pytest.skip(msg) - return test(*args, **kwargs) - return wrapper - - def onlyPy279OrNewer(test): """Skips this test unless you are on Python 2.7.9 or later.""" @@ -109,17 +84,6 @@ def wrapper(*args, **kwargs): return wrapper -def onlyPy27OrNewerOrNonWindows(test): - """Skips this test unless you are on Python2.7+ or non-Windows""" - @functools.wraps(test) - def wrapper(*args, **kwargs): - msg = "{name} requires Python2.7+ or non-Windows to run".format(name=test.__name__) - if sys.version_info < (2, 7) and platform.system() == 'Windows': - pytest.skip(msg) - return test(*args, **kwargs) - return wrapper - - _requires_network_has_route = None diff --git a/test/contrib/test_pyopenssl.py b/test/contrib/test_pyopenssl.py index 20f806071f..7fc296f31b 100644 --- a/test/contrib/test_pyopenssl.py +++ b/test/contrib/test_pyopenssl.py @@ -82,5 +82,5 @@ def test_get_subj_alt_name(self, mock_warning): self.assertEqual(get_subj_alt_name(cert), []) self.assertEqual(mock_warning.call_count, 1) - self.assertTrue(isinstance(mock_warning.call_args[0][1], - x509.DuplicateExtension)) + self.assertIsInstance(mock_warning.call_args[0][1], + x509.DuplicateExtension) diff --git a/test/contrib/test_socks.py b/test/contrib/test_socks.py index 67228cbe4a..ce01645277 100644 --- a/test/contrib/test_socks.py +++ b/test/contrib/test_socks.py @@ -288,7 +288,7 @@ def request_handler(listener): break self.assertTrue(buf.startswith(b'GET / HTTP/1.1')) - self.assertTrue(b'Host: example.com' in buf) + self.assertIn(b'Host: example.com', buf) sock.sendall(b'HTTP/1.1 200 OK\r\n' b'Server: SocksTestServer\r\n' @@ -455,7 +455,7 @@ def request_handler(listener): try: pm.request('GET', 'http://example.com', retries=False) except NewConnectionError as e: - self.assertTrue("SOCKS5 authentication failed" in str(e)) + self.assertIn("SOCKS5 authentication failed", str(e)) else: self.fail("Did not raise") @@ -584,7 +584,7 @@ def request_handler(listener): break self.assertTrue(buf.startswith(b'GET / HTTP/1.1')) - self.assertTrue(b'Host: example.com' in buf) + self.assertIn(b'Host: example.com', buf) sock.sendall(b'HTTP/1.1 200 OK\r\n' b'Server: SocksTestServer\r\n' @@ -670,7 +670,7 @@ def request_handler(listener): try: pm.request('GET', 'http://example.com', retries=False) except NewConnectionError as e: - self.assertTrue("different user-ids" in str(e)) + self.assertIn("different user-ids", str(e)) else: self.fail("Did not raise") diff --git a/test/test_no_ssl.py b/test/test_no_ssl.py index 3f04cd8bd1..4b4eb5594c 100644 --- a/test/test_no_ssl.py +++ b/test/test_no_ssl.py @@ -6,10 +6,7 @@ """ import sys -if sys.version_info >= (2, 7): - import unittest -else: - import unittest2 as unittest +import unittest class ImportBlocker(object): @@ -80,13 +77,8 @@ def tearDown(self): class TestImportWithoutSSL(TestWithoutSSL): def test_cannot_import_ssl(self): - # python26 has neither contextmanagers (for assertRaises) nor - # importlib. - # 'import' inside 'lambda' is invalid syntax. - def import_ssl(): + with self.assertRaises(ImportError): import ssl # noqa: F401 - self.assertRaises(ImportError, import_ssl) - def test_import_urllib3(self): import urllib3 # noqa: F401 diff --git a/test/test_poolmanager.py b/test/test_poolmanager.py index a233f01305..6ed695df4b 100644 --- a/test/test_poolmanager.py +++ b/test/test_poolmanager.py @@ -188,10 +188,7 @@ def test_pools_keyed_with_from_host(self): 'ssl_version': 'SSLv23_METHOD', } p = PoolManager(5, **ssl_kw) - conns = [] - conns.append( - p.connection_from_host('example.com', 443, scheme='https') - ) + conns = [p.connection_from_host('example.com', 443, scheme='https')] for k in ssl_kw: p.connection_pool_kw[k] = 'newval' diff --git a/test/with_dummyserver/test_chunked_transfer.py b/test/with_dummyserver/test_chunked_transfer.py index ba5251b4a2..11fedec260 100644 --- a/test/with_dummyserver/test_chunked_transfer.py +++ b/test/with_dummyserver/test_chunked_transfer.py @@ -31,7 +31,7 @@ def test_chunks(self): pool.urlopen('GET', '/', chunks, headers=dict(DNT='1'), chunked=True) self.addCleanup(pool.close) - self.assertTrue(b'Transfer-Encoding' in self.buffer) + self.assertIn(b'Transfer-Encoding', self.buffer) body = self.buffer.split(b'\r\n\r\n', 1)[1] lines = body.split(b'\r\n') # Empty chunks should have been skipped, as this could not be distinguished @@ -48,10 +48,10 @@ def _test_body(self, data): pool.urlopen('GET', '/', data, chunked=True) header, body = self.buffer.split(b'\r\n\r\n', 1) - self.assertTrue(b'Transfer-Encoding: chunked' in header.split(b'\r\n')) + self.assertIn(b'Transfer-Encoding: chunked', header.split(b'\r\n')) if data: bdata = data if isinstance(data, six.binary_type) else data.encode('utf-8') - self.assertTrue(b'\r\n' + bdata + b'\r\n' in body) + self.assertIn(b'\r\n' + bdata + b'\r\n', body) self.assertTrue(body.endswith(b'\r\n0\r\n\r\n')) len_str = body.split(b'\r\n', 1)[0] diff --git a/test/with_dummyserver/test_connectionpool.py b/test/with_dummyserver/test_connectionpool.py index eb17b40035..5faa063841 100644 --- a/test/with_dummyserver/test_connectionpool.py +++ b/test/with_dummyserver/test_connectionpool.py @@ -120,7 +120,7 @@ def test_timeout(self): block_event.set() # Release request message = "timeout was pool-level LONG_TIMEOUT rather than request-level SHORT_TIMEOUT" - self.assertTrue(delta < LONG_TIMEOUT, message) + self.assertLess(delta, LONG_TIMEOUT, message) pool._put_conn(conn) wait_for_socket(ready_event) @@ -129,7 +129,7 @@ def test_timeout(self): delta = time.time() - now message = "timeout was pool-level LONG_TIMEOUT rather than request-level SHORT_TIMEOUT" - self.assertTrue(delta < LONG_TIMEOUT, message) + self.assertLess(delta, LONG_TIMEOUT, message) block_event.set() # Release request # Timeout int/float passed directly to request and _make_request should @@ -388,10 +388,7 @@ def test_tunnel(self): self.addCleanup(pool.close) conn = pool._get_conn() self.addCleanup(conn.close) - try: - conn.set_tunnel(self.host, self.port) - except AttributeError: # python 2.6 - conn._set_tunnel(self.host, self.port) + conn.set_tunnel(self.host, self.port) conn._tunnel = mock.Mock(return_value=None) pool._make_request(conn, 'GET', '/') @@ -406,7 +403,7 @@ def test_tunnel(self): conn._tunnel = mock.Mock(return_value=None) pool._make_request(conn, 'GET', '/') - self.assertEqual(conn._tunnel.called, False) + self.assertFalse(conn._tunnel.called) def test_redirect(self): r = self.pool.request('GET', '/redirect', fields={'target': '/'}, redirect=False) @@ -454,7 +451,7 @@ def test_keepalive_close(self): # because _get_conn() is where the check & reset occurs # pylint: disable-msg=W0212 conn = pool.pool.get() - self.assertEqual(conn.sock, None) + self.assertIsNone(conn.sock) pool._put_conn(conn) # Now with keep-alive @@ -466,7 +463,7 @@ def test_keepalive_close(self): # The dummyserver responded with Connection:keep-alive, the connection # persists. conn = pool.pool.get() - self.assertNotEqual(conn.sock, None) + self.assertIsNotNone(conn.sock) pool._put_conn(conn) # Another request asking the server to close the connection. This one @@ -479,7 +476,7 @@ def test_keepalive_close(self): self.assertEqual(r.status, 200) conn = pool.pool.get() - self.assertEqual(conn.sock, None) + self.assertIsNone(conn.sock) pool._put_conn(conn) # Next request @@ -932,14 +929,14 @@ def test_redirect_after(self): r = self.pool.request('GET', '/redirect_after') self.assertEqual(r.status, 200) delta = time.time() - t - self.assertTrue(delta >= 1) + self.assertGreaterEqual(delta, 1) t = time.time() timestamp = t + 2 r = self.pool.request('GET', '/redirect_after?date=' + str(timestamp)) self.assertEqual(r.status, 200) delta = time.time() - t - self.assertTrue(delta >= 1) + self.assertGreaterEqual(delta, 1) # Retry-After is past t = time.time() @@ -947,7 +944,7 @@ def test_redirect_after(self): r = self.pool.request('GET', '/redirect_after?date=' + str(timestamp)) delta = time.time() - t self.assertEqual(r.status, 200) - self.assertTrue(delta < 1) + self.assertLess(delta, 1) class TestFileBodiesOnRetryOrRedirect(HTTPDummyServerTestCase): @@ -1005,7 +1002,7 @@ def tell(self): self.pool.urlopen('PUT', url, headers=headers, body=body) self.fail('PUT successful despite failed rewind.') except UnrewindableBodyError as e: - self.assertTrue('Unable to record file position for' in str(e)) + self.assertIn('Unable to record file position for', str(e)) class TestRetryPoolSize(HTTPDummyServerTestCase): diff --git a/test/with_dummyserver/test_https.py b/test/with_dummyserver/test_https.py index 9078151ed9..41c1d5a723 100644 --- a/test/with_dummyserver/test_https.py +++ b/test/with_dummyserver/test_https.py @@ -20,10 +20,8 @@ IP_SAN_CERTS) from test import ( - onlyPy26OrOlder, onlyPy279OrNewer, notSecureTransport, - onlyPy27OrNewerOrNonWindows, requires_network, TARPIT_HOST, ) @@ -232,9 +230,9 @@ def test_verified_with_bad_ca_certs(self): self.fail("Didn't raise SSL error with bad CA certs") except MaxRetryError as e: self.assertIsInstance(e.reason, SSLError) - self.assertTrue('certificate verify failed' in str(e.reason), - "Expected 'certificate verify failed'," - "instead got: %r" % e.reason) + self.assertIn('certificate verify failed', str(e.reason), + "Expected 'certificate verify failed'," + "instead got: %r" % e.reason) def test_verified_without_ca_certs(self): # default is cert_reqs=None which is ssl.CERT_NONE @@ -476,26 +474,11 @@ def test_tunnel(self): self.addCleanup(https_pool.close) conn = https_pool._new_conn() self.addCleanup(conn.close) - try: - conn.set_tunnel(self.host, self.port) - except AttributeError: # python 2.6 - conn._set_tunnel(self.host, self.port) + conn.set_tunnel(self.host, self.port) conn._tunnel = mock.Mock() https_pool._make_request(conn, 'GET', '/') conn._tunnel.assert_called_once_with() - @onlyPy26OrOlder - def test_tunnel_old_python(self): - """HTTPSConnection can still make connections if _tunnel_host isn't set - - The _tunnel_host attribute was added in 2.6.3 - because our test runners - generally use the latest Python 2.6, we simulate the old version by - deleting the attribute from the HTTPSConnection. - """ - conn = self._pool._new_conn() - del conn._tunnel_host - self._pool._make_request(conn, 'GET', '/') - @requires_network def test_enhanced_timeout(self): def new_pool(timeout, cert_reqs='CERT_REQUIRED'): @@ -556,7 +539,7 @@ def test_ssl_wrong_system_time(self): warning = w[0] self.assertEqual(SystemTimeWarning, warning.category) - self.assertTrue(str(RECENT_DATE) in warning.message.args[0]) + self.assertIn(str(RECENT_DATE), warning.message.args[0]) def _request_without_resource_warnings(self, method, url): with warnings.catch_warnings(record=True) as w: @@ -574,11 +557,7 @@ def setUp(self): self._pool = HTTPSConnectionPool(self.host, self.port) self.addCleanup(self._pool.close) - @onlyPy27OrNewerOrNonWindows def test_discards_connection_on_sslerror(self): - # This test is skipped on Windows for Python 2.6 because we suspect there - # is an issue with the OpenSSL for Python 2.6 on Windows. - self._pool.cert_reqs = 'CERT_REQUIRED' with self.assertRaises(MaxRetryError) as cm: self._pool.request('GET', '/', retries=0) diff --git a/test/with_dummyserver/test_no_ssl.py b/test/with_dummyserver/test_no_ssl.py index 956ebe83ef..282c9faaa9 100644 --- a/test/with_dummyserver/test_no_ssl.py +++ b/test/with_dummyserver/test_no_ssl.py @@ -26,4 +26,4 @@ def test_simple(self): try: pool.request('GET', '/') except urllib3.exceptions.SSLError as e: - self.assertTrue('SSL module is not available' in str(e)) + self.assertIn('SSL module is not available', str(e)) diff --git a/test/with_dummyserver/test_poolmanager.py b/test/with_dummyserver/test_poolmanager.py index 448525f61b..2a13722c66 100644 --- a/test/with_dummyserver/test_poolmanager.py +++ b/test/with_dummyserver/test_poolmanager.py @@ -240,12 +240,12 @@ def test_headers(self): r = http.request_encode_url('GET', '%s/headers' % self.base_url, headers={'Baz': 'quux'}) returned_headers = json.loads(r.data.decode()) - self.assertEqual(returned_headers.get('Foo'), None) + self.assertIsNone(returned_headers.get('Foo')) self.assertEqual(returned_headers.get('Baz'), 'quux') r = http.request_encode_body('GET', '%s/headers' % self.base_url, headers={'Baz': 'quux'}) returned_headers = json.loads(r.data.decode()) - self.assertEqual(returned_headers.get('Foo'), None) + self.assertIsNone(returned_headers.get('Foo')) self.assertEqual(returned_headers.get('Baz'), 'quux') def test_http_with_ssl_keywords(self): diff --git a/test/with_dummyserver/test_proxy_poolmanager.py b/test/with_dummyserver/test_proxy_poolmanager.py index 0e1d04bbe0..225f5c4527 100644 --- a/test/with_dummyserver/test_proxy_poolmanager.py +++ b/test/with_dummyserver/test_proxy_poolmanager.py @@ -86,9 +86,9 @@ def test_proxy_verified(self): self.fail("Didn't raise SSL error with wrong CA") except MaxRetryError as e: self.assertIsInstance(e.reason, SSLError) - self.assertTrue('certificate verify failed' in str(e.reason), - "Expected 'certificate verify failed'," - "instead got: %r" % e.reason) + self.assertIn('certificate verify failed', str(e.reason), + "Expected 'certificate verify failed'," + "instead got: %r" % e.reason) http = proxy_from_url(self.proxy_url, cert_reqs='REQUIRED', ca_certs=DEFAULT_CA) @@ -108,7 +108,7 @@ def test_proxy_verified(self): self.fail("Didn't raise SSL invalid common name") except MaxRetryError as e: self.assertIsInstance(e.reason, SSLError) - self.assertTrue("doesn't match" in str(e.reason)) + self.assertIn("doesn't match", str(e.reason)) def test_redirect(self): http = proxy_from_url(self.proxy_url) @@ -186,14 +186,14 @@ def test_headers(self): r = http.request_encode_url('GET', '%s/headers' % self.https_url) returned_headers = json.loads(r.data.decode()) self.assertEqual(returned_headers.get('Foo'), 'bar') - self.assertEqual(returned_headers.get('Hickory'), None) + self.assertIsNone(returned_headers.get('Hickory')) self.assertEqual(returned_headers.get('Host'), '%s:%s' % (self.https_host, self.https_port)) r = http.request_encode_url('GET', '%s/headers' % self.https_url_alt) returned_headers = json.loads(r.data.decode()) self.assertEqual(returned_headers.get('Foo'), 'bar') - self.assertEqual(returned_headers.get('Hickory'), None) + self.assertIsNone(returned_headers.get('Hickory')) self.assertEqual(returned_headers.get('Host'), '%s:%s' % (self.https_host_alt, self.https_port)) @@ -206,7 +206,7 @@ def test_headers(self): r = http.request_encode_url('GET', '%s/headers' % self.http_url, headers={'Baz': 'quux'}) returned_headers = json.loads(r.data.decode()) - self.assertEqual(returned_headers.get('Foo'), None) + self.assertIsNone(returned_headers.get('Foo')) self.assertEqual(returned_headers.get('Baz'), 'quux') self.assertEqual(returned_headers.get('Hickory'), 'dickory') self.assertEqual(returned_headers.get('Host'), @@ -214,15 +214,15 @@ def test_headers(self): r = http.request_encode_url('GET', '%s/headers' % self.https_url, headers={'Baz': 'quux'}) returned_headers = json.loads(r.data.decode()) - self.assertEqual(returned_headers.get('Foo'), None) + self.assertIsNone(returned_headers.get('Foo')) self.assertEqual(returned_headers.get('Baz'), 'quux') - self.assertEqual(returned_headers.get('Hickory'), None) + self.assertIsNone(returned_headers.get('Hickory')) self.assertEqual(returned_headers.get('Host'), '%s:%s' % (self.https_host, self.https_port)) r = http.request_encode_body('GET', '%s/headers' % self.http_url, headers={'Baz': 'quux'}) returned_headers = json.loads(r.data.decode()) - self.assertEqual(returned_headers.get('Foo'), None) + self.assertIsNone(returned_headers.get('Foo')) self.assertEqual(returned_headers.get('Baz'), 'quux') self.assertEqual(returned_headers.get('Hickory'), 'dickory') self.assertEqual(returned_headers.get('Host'), @@ -230,9 +230,9 @@ def test_headers(self): r = http.request_encode_body('GET', '%s/headers' % self.https_url, headers={'Baz': 'quux'}) returned_headers = json.loads(r.data.decode()) - self.assertEqual(returned_headers.get('Foo'), None) + self.assertIsNone(returned_headers.get('Foo')) self.assertEqual(returned_headers.get('Baz'), 'quux') - self.assertEqual(returned_headers.get('Hickory'), None) + self.assertIsNone(returned_headers.get('Hickory')) self.assertEqual(returned_headers.get('Host'), '%s:%s' % (self.https_host, self.https_port)) diff --git a/test/with_dummyserver/test_socketlevel.py b/test/with_dummyserver/test_socketlevel.py index 3135d37149..a1896cfd34 100644 --- a/test/with_dummyserver/test_socketlevel.py +++ b/test/with_dummyserver/test_socketlevel.py @@ -14,7 +14,7 @@ from urllib3.util.ssl_ import HAS_SNI from urllib3.util.timeout import Timeout from urllib3.util.retry import Retry -from urllib3._collections import HTTPHeaderDict, OrderedDict +from urllib3._collections import HTTPHeaderDict from dummyserver.testcase import SocketDummyServerTestCase, consume_socket from dummyserver.server import ( @@ -27,6 +27,7 @@ except ImportError: class MimeToolMessage(object): pass +from collections import OrderedDict from threading import Event import select import socket @@ -81,8 +82,8 @@ def socket_handler(listener): except MaxRetryError: # We are violating the protocol pass done_receiving.wait() - self.assertTrue(self.host.encode('ascii') in self.buf, - "missing hostname in SSL handshake") + self.assertIn(self.host.encode('ascii'), self.buf, + "missing hostname in SSL handshake") class TestClientCerts(SocketDummyServerTestCase): @@ -737,12 +738,12 @@ def socket_handler(listener): # should be in the pool. We opened two though. self.assertEqual(pool.num_connections, 2) self.assertEqual(pool.pool.qsize(), 0) - self.assertTrue(response.connection is not None) + self.assertIsNotNone(response.connection) # Consume the data. This should put the connection back. response.read() self.assertEqual(pool.pool.qsize(), 1) - self.assertTrue(response.connection is None) + self.assertIsNone(response.connection) class TestProxyManager(SocketDummyServerTestCase): @@ -814,7 +815,7 @@ def echo_socket_handler(listener): # FIXME: The order of the headers is not predictable right now. We # should fix that someday (maybe when we migrate to # OrderedDict/MultiDict). - self.assertTrue(b'For The Proxy: YEAH!\r\n' in r.data) + self.assertIn(b'For The Proxy: YEAH!\r\n', r.data) def test_retries(self): close_event = Event() @@ -1433,7 +1434,7 @@ def socket_handler(listener): next(data) self.assertFail() except ProtocolError as e: - self.assertTrue('12 bytes read, 10 more expected' in str(e)) + self.assertIn('12 bytes read, 10 more expected', str(e)) done_event.set() diff --git a/tox.ini b/tox.ini index d52d873056..5f65ab9fa5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = flake8-py3, py26, py27, py34, py35, py36, py37, pypy +envlist = flake8-py3, py27, py34, py35, py36, py37, pypy [testenv] deps= -r{toxinidir}/dev-requirements.txt @@ -20,12 +20,6 @@ setenv = PYTHONWARNINGS=always::DeprecationWarning passenv = CFLAGS LDFLAGS TRAVIS APPVEYOR CRYPTOGRAPHY_OSX_NO_LINK_FLAGS -[testenv:py26] -# Additional dependency on unittest2 for Python 2.6 -deps= - {[testenv]deps} - unittest2 - [testenv:gae] basepython = python2.7 deps=