mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
commit
9127946b8b
|
@ -178,51 +178,6 @@ SSL Certificate Verification
|
|||
Starting with v1.3, pip provides SSL certificate verification over https, for the purpose
|
||||
of providing secure, certified downloads from PyPI.
|
||||
|
||||
This is supported by default in all Python versions pip supports, except Python 2.5.
|
||||
|
||||
Python 2.5 users can :ref:`install an SSL backport <SSL Backport>`, which provides ssl support for older pythons.
|
||||
Pip does not try to install this automatically because it requires a compiler, which not all systems will have.
|
||||
|
||||
Although not recommended, Python 2.5 users who are unable to install ssl, can use the global option,
|
||||
``--insecure``, to allow access to PyPI w/o attempting SSL certificate verification. This option will only be visible
|
||||
when ssl is not importable. This is *not* a general option.
|
||||
|
||||
|
||||
.. _`SSL Backport`:
|
||||
|
||||
Installing the SSL Backport
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. warning::
|
||||
|
||||
We advise against using ``pip`` itself to install the ssl backport, because it won't be secure
|
||||
until *after* installing ssl. Likewise, `easy_install <http://pythonhosted.org/distribute/easy_install.html>`_ is not advised, because it
|
||||
does not currently support ssl.
|
||||
|
||||
|
||||
1. Download the ssl archive:
|
||||
|
||||
* Using a Browser:
|
||||
|
||||
1. Go to `this url <https://pypi.python.org/pypi/ssl/1.15>`_.
|
||||
2. Confirm the identity of the site is valid.
|
||||
Most browsers provide this information to the left of the URL bar in the form of padlock icon that you can click on to confirm the site is verified.
|
||||
3. Scroll down, and click to download ``ssl-1.15.tar.gz``.
|
||||
|
||||
* Using curl, which supports ssl certificate verification:
|
||||
::
|
||||
|
||||
$ curl -O https://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz
|
||||
|
||||
2. Confirm the md5sum:
|
||||
::
|
||||
|
||||
$ md5sum ssl-1.15.tar.gz
|
||||
81ea8a1175e437b4c769ae65b3290e0c ssl-1.15.tar.gz
|
||||
|
||||
3. Unpack the archive, and change into the ``ssl-1.15`` directory.
|
||||
4. Run: ``python setup.py install``.
|
||||
|
||||
|
||||
Hash Verification
|
||||
=================
|
||||
|
|
|
@ -9,7 +9,6 @@ __all__ = ['WindowsError']
|
|||
|
||||
uses_pycache = hasattr(imp, 'cache_from_source')
|
||||
|
||||
|
||||
class NeverUsedException(Exception):
|
||||
"""this exception should never be raised"""
|
||||
|
||||
|
@ -128,22 +127,8 @@ def home_lib(home):
|
|||
return os.path.join(home, lib)
|
||||
|
||||
|
||||
## py25 has no builtin ssl module
|
||||
## only >=py32 has ssl.match_hostname and ssl.CertificateError
|
||||
try:
|
||||
import ssl
|
||||
try:
|
||||
from ssl import match_hostname, CertificateError
|
||||
except ImportError:
|
||||
from pip.backwardcompat.ssl_match_hostname import match_hostname, CertificateError
|
||||
from ssl import match_hostname, CertificateError
|
||||
except ImportError:
|
||||
ssl = None
|
||||
|
||||
|
||||
# patch for py25 socket to work with http://pypi.python.org/pypi/ssl/
|
||||
import socket
|
||||
if not hasattr(socket, 'create_connection'): # for Python 2.5
|
||||
# monkey-patch socket module
|
||||
from pip.backwardcompat.socket_create_connection import create_connection
|
||||
socket.create_connection = create_connection
|
||||
|
||||
from ssl_match_hostname import match_hostname, CertificateError
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
"""
|
||||
patch for py25 socket to work with http://pypi.python.org/pypi/ssl/
|
||||
copy-paste from py2.6 stdlib socket.py
|
||||
https://gist.github.com/zed/1347055
|
||||
"""
|
||||
import socket
|
||||
import sys
|
||||
|
||||
_GLOBAL_DEFAULT_TIMEOUT = getattr(socket, '_GLOBAL_DEFAULT_TIMEOUT', object())
|
||||
def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
|
||||
source_address=None):
|
||||
"""Connect to *address* and return the socket object.
|
||||
|
||||
Convenience function. Connect to *address* (a 2-tuple ``(host,
|
||||
port)``) and return the socket object. Passing the optional
|
||||
*timeout* parameter will set the timeout on the socket instance
|
||||
before attempting to connect. If no *timeout* is supplied, the
|
||||
global default timeout setting returned by :func:`getdefaulttimeout`
|
||||
is used.
|
||||
"""
|
||||
|
||||
host, port = address
|
||||
err = None
|
||||
for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
sock = None
|
||||
try:
|
||||
sock = socket.socket(af, socktype, proto)
|
||||
if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
|
||||
sock.settimeout(timeout)
|
||||
if source_address:
|
||||
sock.bind(source_address)
|
||||
sock.connect(sa)
|
||||
return sock
|
||||
|
||||
except socket.error:
|
||||
err = sys.exc_info()[1]
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
|
||||
if err is not None:
|
||||
raise err
|
||||
else:
|
||||
raise socket.error("getaddrinfo returns an empty list")
|
|
@ -12,7 +12,7 @@ from pip.log import logger
|
|||
from pip.download import urlopen
|
||||
from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
|
||||
CommandError)
|
||||
from pip.backwardcompat import StringIO, ssl
|
||||
from pip.backwardcompat import StringIO
|
||||
from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
||||
from pip.status_codes import SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND
|
||||
from pip.util import get_prog
|
||||
|
@ -75,8 +75,6 @@ class Command(object):
|
|||
'skip_requirements_regex',
|
||||
'no_input', 'exists_action',
|
||||
'cert']
|
||||
if not ssl:
|
||||
attrs.append('insecure')
|
||||
for attr in attrs:
|
||||
setattr(options, attr, getattr(initial_options, attr) or getattr(options, attr))
|
||||
options.quiet += initial_options.quiet
|
||||
|
@ -111,9 +109,6 @@ class Command(object):
|
|||
if options.exists_action:
|
||||
os.environ['PIP_EXISTS_ACTION'] = ''.join(options.exists_action)
|
||||
|
||||
if not ssl and options.insecure:
|
||||
os.environ['PIP_INSECURE'] = '1'
|
||||
|
||||
if options.cert:
|
||||
os.environ['PIP_CERT'] = options.cert
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import pkg_resources
|
|||
import os
|
||||
import textwrap
|
||||
from distutils.util import strtobool
|
||||
from pip.backwardcompat import ConfigParser, string_types, ssl
|
||||
from pip.backwardcompat import ConfigParser, string_types
|
||||
from pip.locations import default_config_file, default_log_file
|
||||
from pip.util import get_terminal_size, get_prog
|
||||
|
||||
|
@ -366,11 +366,3 @@ standard_options = [
|
|||
help = "Path to alternate CA bundle."),
|
||||
|
||||
]
|
||||
|
||||
if not ssl:
|
||||
standard_options.append(optparse.make_option(
|
||||
'--insecure',
|
||||
dest='insecure',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help = "Allow lack of certificate checking when ssl is not installed."))
|
||||
|
|
|
@ -224,10 +224,6 @@ class InstallCommand(Command):
|
|||
logger.warn(msg)
|
||||
return
|
||||
|
||||
if (options.use_user_site and
|
||||
sys.version_info < (2, 6)):
|
||||
raise InstallationError('--user is only supported in Python version 2.6 and newer')
|
||||
|
||||
import setuptools
|
||||
if (options.use_user_site and
|
||||
requirement_set.has_editables and
|
||||
|
|
|
@ -81,9 +81,7 @@ class WheelCommand(Command):
|
|||
|
||||
def run(self, options, args):
|
||||
|
||||
# requirements: py26, wheel, and distribute
|
||||
if sys.version_info < (2, 6):
|
||||
raise CommandError("'pip wheel' requires Python 2.6 or greater.")
|
||||
# requirements: wheel, and distribute
|
||||
try:
|
||||
import wheel.bdist_wheel
|
||||
except ImportError:
|
||||
|
|
|
@ -6,14 +6,14 @@ import os
|
|||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import ssl
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from pip.backwardcompat import (xmlrpclib, urllib, urllib2, httplib,
|
||||
urlparse, string_types, ssl, get_http_message_param)
|
||||
if ssl:
|
||||
from pip.backwardcompat import match_hostname, CertificateError
|
||||
from pip.exceptions import InstallationError, PipError, NoSSLError
|
||||
urlparse, string_types, get_http_message_param,
|
||||
match_hostname, CertificateError)
|
||||
from pip.exceptions import InstallationError, PipError
|
||||
from pip.util import (splitext, rmtree, format_size, display_path,
|
||||
backup_dir, ask_path_exists, unpack_file,
|
||||
create_download_cache_folder, cache_download)
|
||||
|
@ -185,8 +185,7 @@ class URLOpener(object):
|
|||
|
||||
def get_opener(self, *args, **kwargs):
|
||||
"""
|
||||
Build an OpenerDirector instance based on the scheme, whether ssl is
|
||||
importable and the --insecure parameter.
|
||||
Build an OpenerDirector instance based on the scheme and proxy option
|
||||
"""
|
||||
|
||||
args = list(args)
|
||||
|
@ -194,18 +193,13 @@ class URLOpener(object):
|
|||
args.extend([self.proxy_handler, urllib2.CacheFTPHandler])
|
||||
|
||||
if kwargs.get('scheme') == 'https':
|
||||
if ssl:
|
||||
https_handler = VerifiedHTTPSHandler()
|
||||
director = urllib2.build_opener(https_handler, *args)
|
||||
#strip out HTTPHandler to prevent MITM spoof
|
||||
for handler in director.handlers:
|
||||
if isinstance(handler, urllib2.HTTPHandler):
|
||||
director.handlers.remove(handler)
|
||||
return director
|
||||
elif os.environ.get('PIP_INSECURE', '') == '1':
|
||||
return urllib2.build_opener(*args)
|
||||
else:
|
||||
raise NoSSLError()
|
||||
https_handler = VerifiedHTTPSHandler()
|
||||
director = urllib2.build_opener(https_handler, *args)
|
||||
#strip out HTTPHandler to prevent MITM spoof
|
||||
for handler in director.handlers:
|
||||
if isinstance(handler, urllib2.HTTPHandler):
|
||||
director.handlers.remove(handler)
|
||||
return director
|
||||
else:
|
||||
return urllib2.build_opener(*args)
|
||||
|
||||
|
|
|
@ -29,28 +29,3 @@ class BadCommand(PipError):
|
|||
|
||||
class CommandError(PipError):
|
||||
"""Raised when there is an error in command-line arguments"""
|
||||
|
||||
|
||||
class NoSSLError(PipError):
|
||||
"""Raised when there's no ssl and not using '--insecure'"""
|
||||
|
||||
def __str__(self):
|
||||
return textwrap.dedent("""
|
||||
###################################################################
|
||||
## You don't have an importable ssl module. You are most ##
|
||||
## likely using Python 2.5, which did not include ssl ##
|
||||
## support by default. In this state, we can not provide ##
|
||||
## ssl certified downloads from PyPI. ##
|
||||
## ##
|
||||
## You can do one of 2 things: ##
|
||||
## 1) Install this: https://pypi.python.org/pypi/ssl/ ##
|
||||
## (It provides ssl support for older Pythons ) ##
|
||||
## 2) Use the --insecure option to allow this insecurity ##
|
||||
## ##
|
||||
## For more details, go to the "SSL Certificate Verification" ##
|
||||
## section located here: ##
|
||||
## http://www.pip-installer.org/en/latest/logic.html ##
|
||||
## ##
|
||||
###################################################################
|
||||
""")
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import posixpath
|
|||
import pkg_resources
|
||||
import random
|
||||
import socket
|
||||
import ssl
|
||||
import string
|
||||
import zlib
|
||||
|
||||
|
@ -24,10 +25,9 @@ from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled,\
|
|||
from pip.backwardcompat import (WindowsError, BytesIO,
|
||||
Queue, urlparse,
|
||||
URLError, HTTPError, u,
|
||||
product, url2pathname, ssl,
|
||||
product, url2pathname,
|
||||
Empty as QueueEmpty)
|
||||
if ssl:
|
||||
from pip.backwardcompat import CertificateError
|
||||
from pip.backwardcompat import CertificateError
|
||||
from pip.download import urlopen, path_to_url2, url_to_path, geturl, Urllib2HeadRequest
|
||||
import pip.pep425tags
|
||||
import pip.wheel
|
||||
|
@ -554,7 +554,7 @@ class HTMLPage(object):
|
|||
desc = 'timed out'
|
||||
elif isinstance(e, URLError):
|
||||
#ssl/certificate error
|
||||
if ssl and hasattr(e, 'reason') and (isinstance(e.reason, ssl.SSLError) or isinstance(e.reason, CertificateError)):
|
||||
if hasattr(e, 'reason') and (isinstance(e.reason, ssl.SSLError) or isinstance(e.reason, CertificateError)):
|
||||
desc = 'There was a problem confirming the ssl certificate: %s' % e
|
||||
log_meth = logger.notify
|
||||
else:
|
||||
|
|
|
@ -110,8 +110,7 @@ def distutils_scheme(dist_name, user=False, home=None):
|
|||
scheme = {}
|
||||
d = Distribution({'name': dist_name})
|
||||
i = install(d)
|
||||
if sys.version_info >= (2, 6):
|
||||
i.user = user or i.user
|
||||
i.user = user or i.user
|
||||
i.home = home or i.home
|
||||
i.finalize_options()
|
||||
for key in SCHEME_KEYS:
|
||||
|
|
|
@ -11,7 +11,7 @@ import subprocess
|
|||
import textwrap
|
||||
from pip.exceptions import InstallationError, BadCommand, PipError
|
||||
from pip.backwardcompat import(WindowsError, string_types, raw_input,
|
||||
console_to_str, user_site, ssl)
|
||||
console_to_str, user_site)
|
||||
from pip.locations import site_packages, running_under_virtualenv, virtualenv_no_global
|
||||
from pip.log import logger
|
||||
from pip.vendor.distlib import version
|
||||
|
|
|
@ -10,7 +10,6 @@ from mock import patch
|
|||
|
||||
from pip.util import rmtree, find_command
|
||||
from pip.exceptions import BadCommand
|
||||
from pip.backwardcompat import ssl
|
||||
|
||||
from tests.test_pip import (here, reset_env, run_pip, pyversion, mkdir,
|
||||
src_folder, write_file, path_to_url)
|
||||
|
@ -51,8 +50,6 @@ def test_pip_second_command_line_interface_works():
|
|||
e = reset_env()
|
||||
|
||||
args = ['pip-%s' % pyversion]
|
||||
if not ssl:
|
||||
args.append('--insecure')
|
||||
args.extend(['install', 'INITools==0.2'])
|
||||
result = e.run(*args)
|
||||
egg_info_folder = e.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion
|
||||
|
|
|
@ -13,11 +13,7 @@ import site
|
|||
from scripttest import TestFileEnvironment, FoundDir
|
||||
from tests.path import Path, curdir, u
|
||||
from pip.util import rmtree
|
||||
from pip.backwardcompat import ssl, uses_pycache
|
||||
|
||||
#allow py25 unit tests to work
|
||||
if sys.version_info[:2] == (2, 5) and not ssl:
|
||||
os.environ['PIP_INSECURE'] = '1'
|
||||
from pip.backwardcompat import uses_pycache
|
||||
|
||||
pyversion = sys.version[:3]
|
||||
pyversion_nodot = "%d%d" % (sys.version_info[0], sys.version_info[1])
|
||||
|
@ -144,10 +140,6 @@ def reset_env(environ=None,
|
|||
(env.lib_path/'no-global-site-packages.txt').rm()
|
||||
test_class.rebuild_venv = True
|
||||
|
||||
if sys.version_info[:2] == (2, 5) and insecure:
|
||||
#allow py25 tests to work
|
||||
env.environ['PIP_INSECURE'] = '1'
|
||||
|
||||
return env
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import os
|
|||
import pip
|
||||
import getpass
|
||||
from pip.basecommand import get_proxy
|
||||
from pip.backwardcompat import urllib2, ssl
|
||||
from pip.backwardcompat import urllib2
|
||||
from pip.download import urlopen, VerifiedHTTPSHandler
|
||||
from tests.test_pip import here
|
||||
|
||||
|
@ -79,28 +79,15 @@ def test_proxy_handlers_present():
|
|||
|
||||
handler_types = [h.__class__ for h in o.handlers]
|
||||
|
||||
if not ssl:
|
||||
assert handler_types == [
|
||||
urllib2.ProxyHandler, # this is needed
|
||||
urllib2.UnknownHandler,
|
||||
urllib2.HTTPHandler,
|
||||
urllib2.HTTPDefaultErrorHandler,
|
||||
urllib2.HTTPRedirectHandler,
|
||||
urllib2.FileHandler,
|
||||
urllib2.HTTPSHandler,
|
||||
urllib2.CacheFTPHandler, # and this
|
||||
urllib2.HTTPErrorProcessor
|
||||
], str(handler_types)
|
||||
else:
|
||||
assert handler_types == [
|
||||
urllib2.ProxyHandler, # this is needed
|
||||
urllib2.UnknownHandler,
|
||||
urllib2.HTTPDefaultErrorHandler,
|
||||
urllib2.HTTPRedirectHandler,
|
||||
urllib2.FileHandler,
|
||||
VerifiedHTTPSHandler,
|
||||
urllib2.CacheFTPHandler, # and this
|
||||
urllib2.HTTPErrorProcessor
|
||||
], str(handler_types)
|
||||
assert handler_types == [
|
||||
urllib2.ProxyHandler, # this is needed
|
||||
urllib2.UnknownHandler,
|
||||
urllib2.HTTPDefaultErrorHandler,
|
||||
urllib2.HTTPRedirectHandler,
|
||||
urllib2.FileHandler,
|
||||
VerifiedHTTPSHandler,
|
||||
urllib2.CacheFTPHandler, # and this
|
||||
urllib2.HTTPErrorProcessor
|
||||
], str(handler_types)
|
||||
|
||||
|
||||
|
|
|
@ -1,91 +1,24 @@
|
|||
|
||||
import sys
|
||||
import os
|
||||
import ssl
|
||||
from mock import Mock, patch
|
||||
from pip.download import urlopen, VerifiedHTTPSHandler
|
||||
from tests.test_pip import assert_raises_regexp, here, reset_env, run_pip
|
||||
from nose import SkipTest
|
||||
from nose.tools import assert_raises
|
||||
from pip.backwardcompat import urllib2, ssl, URLError
|
||||
if ssl:
|
||||
from pip.backwardcompat import CertificateError
|
||||
from pip.backwardcompat import urllib2, URLError
|
||||
from pip.backwardcompat import CertificateError
|
||||
from pip.exceptions import PipError
|
||||
|
||||
pypi_https = 'https://pypi.python.org/simple/'
|
||||
pypi_http = 'http://pypi.python.org/simple/'
|
||||
|
||||
class Tests_py25:
|
||||
"""py25 tests"""
|
||||
class TestsSSL:
|
||||
"""ssl tests"""
|
||||
|
||||
def setup(self):
|
||||
if sys.version_info >= (2, 6):
|
||||
raise SkipTest()
|
||||
|
||||
def teardown(self):
|
||||
#make sure this is set back for other tests
|
||||
os.environ['PIP_INSECURE'] = '1'
|
||||
|
||||
def test_https_fails(self):
|
||||
"""
|
||||
Test py25 access https fails
|
||||
"""
|
||||
if ssl:
|
||||
#travis installs the backport in py25
|
||||
raise SkipTest()
|
||||
os.environ['PIP_INSECURE'] = ''
|
||||
assert_raises_regexp(PipError, 'ssl certified', urlopen.get_opener, scheme='https')
|
||||
|
||||
def test_https_ok_with_flag(self):
|
||||
"""
|
||||
Test py25 access https url ok with --insecure flag
|
||||
This doesn't mean it's doing cert verification, just accessing over https
|
||||
"""
|
||||
os.environ['PIP_INSECURE'] = '1'
|
||||
response = urlopen.get_opener().open(pypi_https)
|
||||
assert response.code == 200, str(dir(response))
|
||||
|
||||
def test_http_ok(self):
|
||||
"""
|
||||
Test http pypi access with pip urlopener
|
||||
"""
|
||||
os.environ['PIP_INSECURE'] = ''
|
||||
response = urlopen.get_opener().open(pypi_http)
|
||||
assert response.code == 200, str(dir(response))
|
||||
|
||||
def test_install_fails_with_no_ssl_backport(self):
|
||||
"""
|
||||
Test installing w/o ssl backport fails
|
||||
"""
|
||||
reset_env(insecure=False)
|
||||
#expect error because ssl's setup.py is hard coded to install test data to global prefix
|
||||
result = run_pip('install', 'INITools', expect_error=True)
|
||||
assert "You don't have an importable ssl module" in result.stdout
|
||||
|
||||
def test_install_with_ssl_backport(self):
|
||||
"""
|
||||
Test installing with ssl backport
|
||||
"""
|
||||
|
||||
#unable to get ssl backport to install on travis.
|
||||
raise SkipTest()
|
||||
|
||||
# insecure=True so we can install ssl first
|
||||
env = reset_env(insecure=True)
|
||||
#expect error because ssl's setup.py is hard coded to install test data to global prefix
|
||||
result = run_pip('install', 'ssl', expect_error=True)
|
||||
|
||||
#set it back to false
|
||||
env.environ['PIP_INSECURE'] = ''
|
||||
result = run_pip('install', 'INITools', expect_error=True)
|
||||
result.assert_installed('initools', editable=False)
|
||||
|
||||
|
||||
class Tests_not_py25:
|
||||
"""non py25 tests"""
|
||||
|
||||
def setup(self):
|
||||
if sys.version_info < (2, 6):
|
||||
raise SkipTest()
|
||||
pass
|
||||
|
||||
def teardown(self):
|
||||
os.environ['PIP_CERT'] = ''
|
||||
|
|
|
@ -18,24 +18,9 @@ patch_dist_in_site_packages = """
|
|||
"""
|
||||
|
||||
|
||||
def test_install_curdir_usersite_fails_in_old_python():
|
||||
"""
|
||||
Test --user option on older Python versions (pre 2.6) fails intelligibly
|
||||
"""
|
||||
if sys.version_info >= (2, 6):
|
||||
raise SkipTest()
|
||||
reset_env(system_site_packages=True)
|
||||
run_from = abspath(join(here, 'packages', 'FSPkg'))
|
||||
result = run_pip('install', '--user', curdir, cwd=run_from, expect_error=True)
|
||||
assert '--user is only supported in Python version 2.6 and newer' in result.stdout
|
||||
|
||||
|
||||
class Tests_UserSite:
|
||||
|
||||
def setup(self):
|
||||
# --user only works on 2.6 or higher
|
||||
if sys.version_info < (2, 6):
|
||||
raise SkipTest()
|
||||
# --user option is broken in pypy
|
||||
if hasattr(sys, "pypy_version_info"):
|
||||
raise SkipTest()
|
||||
|
|
|
@ -45,10 +45,6 @@ def test_uninstallation_paths():
|
|||
|
||||
class TestPipWheel:
|
||||
|
||||
def setup(self):
|
||||
if sys.version_info < (2, 6):
|
||||
raise SkipTest() #bdist_wheel fails in py25?
|
||||
|
||||
def test_pip_wheel_fails_without_wheel(self):
|
||||
"""
|
||||
Test 'pip wheel' fails without wheel
|
||||
|
|
Loading…
Reference in a new issue