test_install.py 59.2 KB
Newer Older
1
import distutils
P
Pradyun S. Gedam 已提交
2
import glob
3
import os
4
import sys
5
import textwrap
P
Pradyun S. Gedam 已提交
6
from os.path import curdir, join, pardir
7

8
import pytest
9

10
import pip
11
from pip._internal import pep425tags
12
from pip._internal.cli.status_codes import ERROR, SUCCESS
13
from pip._internal.models.index import PyPI, TestPyPI
14
from pip._internal.utils.misc import rmtree
P
Pradyun S. Gedam 已提交
15
from tests.lib import (
16 17 18 19 20 21 22 23 24 25
    _create_svn_repo,
    _create_test_package,
    create_basic_wheel_for_package,
    create_test_package_with_setup,
    need_bzr,
    need_mercurial,
    path_to_url,
    pyversion,
    pyversion_tuple,
    requirements_file,
P
Pradyun S. Gedam 已提交
26
)
M
Marcus Smith 已提交
27 28
from tests.lib.local_repos import local_checkout
from tests.lib.path import Path
29 30


B
Benoit Pierre 已提交
31 32 33 34
@pytest.mark.parametrize('command', ('install', 'wheel'))
@pytest.mark.parametrize('variant', ('missing_setuptools', 'bad_setuptools'))
def test_pep518_uses_build_env(script, data, common_wheels, command, variant):
    if variant == 'missing_setuptools':
B
Benoit Pierre 已提交
35
        script.pip("uninstall", "-y", "setuptools")
B
Benoit Pierre 已提交
36
    elif variant == 'bad_setuptools':
37
        setuptools_mod = script.site_packages_path.joinpath("setuptools.py")
38
        with open(setuptools_mod, 'a') as f:
B
Benoit Pierre 已提交
39 40
            f.write('\nraise ImportError("toto")')
    else:
B
Benoit Pierre 已提交
41 42 43
        raise ValueError(variant)
    script.pip(
        command, '--no-index', '-f', common_wheels, '-f', data.packages,
44
        data.src.joinpath("pep518-3.0"),
B
Benoit Pierre 已提交
45
    )
46 47


48 49
def test_pep518_build_env_uses_same_pip(
        script, data, pip_src, common_wheels, deprecated_python):
50 51 52 53 54 55 56 57
    """Ensure the subprocess call to pip for installing the
    build dependencies is using the same version of pip.
    """
    with open(script.scratch_path / 'pip.py', 'w') as fp:
        fp.write('raise ImportError')
    script.run(
        'python', pip_src / 'src/pip', 'install', '--no-index',
        '-f', common_wheels, '-f', data.packages,
58
        data.src.joinpath("pep518-3.0"),
59
        expect_stderr=deprecated_python,
60 61 62
    )


B
Benoit Pierre 已提交
63 64 65
def test_pep518_refuses_conflicting_requires(script, data):
    create_basic_wheel_for_package(script, 'setuptools', '1.0')
    create_basic_wheel_for_package(script, 'wheel', '1.0')
66
    project_dir = data.src.joinpath("pep518_conflicting_requires")
B
Benoit Pierre 已提交
67 68 69 70 71 72
    result = script.pip_install_local('-f', script.scratch_path,
                                      project_dir, expect_error=True)
    assert (
        result.returncode != 0 and
        ('Some build dependencies for %s conflict with PEP 517/518 supported '
         'requirements: setuptools==1.0 is incompatible with '
73
         'setuptools>=40.8.0.' % path_to_url(project_dir)) in result.stderr
B
Benoit Pierre 已提交
74 75 76
    ), str(result)


77
def test_pep518_refuses_invalid_requires(script, data, common_wheels):
78
    result = script.pip(
79
        'install', '-f', common_wheels,
80
        data.src.joinpath("pep518_invalid_requires"),
81 82 83 84 85 86
        expect_error=True
    )
    assert result.returncode == 1
    assert "does not comply with PEP 518" in result.stderr


87 88 89
def test_pep518_refuses_invalid_build_system(script, data, common_wheels):
    result = script.pip(
        'install', '-f', common_wheels,
90
        data.src.joinpath("pep518_invalid_build_system"),
91 92 93 94 95 96
        expect_error=True
    )
    assert result.returncode == 1
    assert "does not comply with PEP 518" in result.stderr


97
def test_pep518_allows_missing_requires(script, data, common_wheels):
98 99
    result = script.pip(
        'install', '-f', common_wheels,
100
        data.src.joinpath("pep518_missing_requires"),
101 102
        expect_stderr=True
    )
103 104
    # Make sure we don't warn when this occurs.
    assert "does not comply with PEP 518" not in result.stderr
105 106 107 108

    # We want it to go through isolation for now.
    assert "Installing build dependencies" in result.stdout, result.stdout

109 110 111 112
    assert result.returncode == 0
    assert result.files_created


113
def test_pep518_with_user_pip(script, pip_src, data, common_wheels):
114 115 116 117 118 119 120 121 122
    """
    Check that build dependencies are installed into the build
    environment without using build isolation for the pip invocation.

    To ensure that we're not using build isolation when installing
    the build dependencies, we install a user copy of pip in the
    non-isolated environment, and break pip in the system site-packages,
    so that isolated uses of pip will fail.
    """
B
Benoit Pierre 已提交
123 124
    script.pip("install", "--ignore-installed",
               "-f", common_wheels, "--user", pip_src)
125
    system_pip_dir = script.site_packages_path / 'pip'
126
    assert not system_pip_dir.exists()
127 128 129
    system_pip_dir.mkdir()
    with open(system_pip_dir / '__init__.py', 'w') as fp:
        fp.write('raise ImportError\n')
B
Benoit Pierre 已提交
130 131
    script.pip(
        'wheel', '--no-index', '-f', common_wheels, '-f', data.packages,
132
        data.src.joinpath("pep518-3.0"),
B
Benoit Pierre 已提交
133
    )
134 135


136 137 138 139 140
def test_pep518_with_extra_and_markers(script, data, common_wheels):
    script.pip(
        'wheel', '--no-index',
        '-f', common_wheels,
        '-f', data.find_links,
141
        data.src.joinpath("pep518_with_extra_and_markers-1.0"),
142 143 144
    )


145 146 147 148 149
def test_pep518_with_namespace_package(script, data, common_wheels):
    script.pip(
        'wheel', '--no-index',
        '-f', common_wheels,
        '-f', data.find_links,
150
        data.src.joinpath("pep518_with_namespace_package-1.0"),
151 152 153 154
        use_module=True,
    )


155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
@pytest.mark.timeout(60)
@pytest.mark.parametrize('command', ('install', 'wheel'))
@pytest.mark.parametrize('package', ('pep518_forkbomb',
                                     'pep518_twin_forkbombs_first',
                                     'pep518_twin_forkbombs_second'))
def test_pep518_forkbombs(script, data, common_wheels, command, package):
    package_source = next(data.packages.glob(package + '-[0-9]*.tar.gz'))
    result = script.pip(
        'wheel', '--no-index', '-v',
        '-f', common_wheels,
        '-f', data.find_links,
        package,
        expect_error=True,
    )
    assert '{1} is already being built: {0} from {1}'.format(
        package, path_to_url(package_source),
171
    ) in result.stderr, str(result)
172 173


X
Xavier Fernandez 已提交
174
@pytest.mark.network
175 176
def test_pip_second_command_line_interface_works(
        script, pip_src, data, common_wheels, deprecated_python):
177
    """
P
Paul Nasrat 已提交
178
    Check if ``pip<PYVERSION>`` commands behaves equally
179
    """
180
    # Re-install pip so we get the launchers.
B
Benoit Pierre 已提交
181
    script.pip_install_local('-f', common_wheels, pip_src)
182 183
    # On old versions of Python, urllib3/requests will raise a warning about
    # the lack of an SSLContext.
184
    kwargs = {'expect_stderr': deprecated_python}
D
Donald Stufft 已提交
185
    if pyversion_tuple < (2, 7, 9):
186 187
        kwargs['expect_stderr'] = True

P
Paul Nasrat 已提交
188
    args = ['pip%s' % pyversion]
189
    args.extend(['install', 'INITools==0.2'])
190
    args.extend(['-f', data.packages])
191
    result = script.run(*args, **kwargs)
192 193 194
    egg_info_folder = (
        script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion
    )
D
Donald Stufft 已提交
195
    initools_folder = script.site_packages / 'initools'
196 197 198 199
    assert egg_info_folder in result.files_created, str(result)
    assert initools_folder in result.files_created, str(result)


200 201 202 203 204 205 206 207 208 209 210 211 212
def test_install_exit_status_code_when_no_requirements(script):
    """
    Test install exit status code when no requirements specified
    """
    result = script.pip('install', expect_error=True)
    assert "You must give at least one requirement to install" in result.stderr
    assert result.returncode == ERROR


def test_install_exit_status_code_when_blank_requirements_file(script):
    """
    Test install exit status code when blank requirements file specified
    """
213
    script.scratch_path.joinpath("blank.txt").write_text("\n")
214 215 216
    script.pip('install', '-r', 'blank.txt')


217
@pytest.mark.network
218
def test_basic_install_from_pypi(script):
219 220 221
    """
    Test installing a package from PyPI.
    """
D
Donald Stufft 已提交
222
    result = script.pip('install', '-vvv', 'INITools==0.2')
223 224 225
    egg_info_folder = (
        script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion
    )
D
Donald Stufft 已提交
226
    initools_folder = script.site_packages / 'initools'
227 228
    assert egg_info_folder in result.files_created, str(result)
    assert initools_folder in result.files_created, str(result)
229

230 231 232 233
    # Should not display where it's looking for files
    assert "Looking in indexes: " not in result.stdout
    assert "Looking in links: " not in result.stdout

234

235
def test_basic_editable_install(script):
236 237 238
    """
    Test editable installation.
    """
D
Donald Stufft 已提交
239
    result = script.pip('install', '-e', 'INITools==0.2', expect_error=True)
240
    assert (
241
        "INITools==0.2 is not a valid editable requirement"
242
        in result.stderr
243
    )
244 245
    assert not result.files_created
    assert not result.files_updated
246

247

X
Xavier Fernandez 已提交
248
@pytest.mark.svn
249
def test_basic_install_editable_from_svn(script):
250 251 252
    """
    Test checking out from svn.
    """
253 254
    checkout_path = _create_test_package(script)
    repo_url = _create_svn_repo(script, checkout_path)
255 256
    result = script.pip(
        'install',
257
        '-e', 'svn+' + repo_url + '#egg=version-pkg'
258
    )
259
    result.assert_installed('version-pkg', with_files=['.svn'])
260

261

262
def _test_install_editable_from_git(script, tmpdir):
263 264 265
    """Test cloning from Git."""
    pkg_path = _create_test_package(script, name='testpackage', vcs='git')
    args = ['install', '-e', 'git+%s#egg=testpackage' % path_to_url(pkg_path)]
C
Chris Hunt 已提交
266
    result = script.pip(*args)
267
    result.assert_installed('testpackage', with_files=['.git'])
268

269

270
def test_basic_install_editable_from_git(script, tmpdir):
271
    _test_install_editable_from_git(script, tmpdir)
272 273


274
def test_install_editable_from_git_autobuild_wheel(
275
        script, tmpdir, with_wheel):
276
    _test_install_editable_from_git(script, tmpdir)
277 278


X
Xavier Fernandez 已提交
279
@pytest.mark.network
280 281 282 283 284 285 286
def test_install_editable_uninstalls_existing(data, script, tmpdir):
    """
    Test that installing an editable uninstalls a previously installed
    non-editable version.
    https://github.com/pypa/pip/issues/1548
    https://github.com/pypa/pip/pull/1552
    """
287
    to_install = data.packages.joinpath("pip-test-package-0.1.tar.gz")
288 289 290 291 292 293 294 295
    result = script.pip_install_local(to_install)
    assert 'Successfully installed pip-test-package' in result.stdout
    result.assert_installed('piptestpackage', editable=False)

    result = script.pip(
        'install', '-e',
        '%s#egg=pip-test-package' %
        local_checkout(
296
            'git+https://github.com/pypa/pip-test-package.git',
297
            tmpdir.joinpath("cache"),
298 299 300 301
        ),
    )
    result.assert_installed('pip-test-package', with_files=['.git'])
    assert 'Found existing installation: pip-test-package 0.1' in result.stdout
302
    assert 'Uninstalling pip-test-package-' in result.stdout
303 304 305
    assert 'Successfully uninstalled pip-test-package' in result.stdout


306 307 308 309 310
def test_install_editable_uninstalls_existing_from_path(script, data):
    """
    Test that installing an editable uninstalls a previously installed
    non-editable version from path
    """
311
    to_install = data.src.joinpath('simplewheel-1.0')
312 313
    result = script.pip_install_local(to_install)
    assert 'Successfully installed simplewheel' in result.stdout
314 315
    simple_folder = script.site_packages / 'simplewheel'
    result.assert_installed('simplewheel', editable=False)
316 317 318 319 320 321 322 323 324 325 326 327 328 329
    assert simple_folder in result.files_created, str(result.stdout)

    result = script.pip(
        'install', '-e',
        to_install,
    )
    install_path = script.site_packages / 'simplewheel.egg-link'
    assert install_path in result.files_created, str(result)
    assert 'Found existing installation: simplewheel 1.0' in result.stdout
    assert 'Uninstalling simplewheel-' in result.stdout
    assert 'Successfully uninstalled simplewheel' in result.stdout
    assert simple_folder in result.files_deleted, str(result.stdout)


330
@need_mercurial
331
def test_basic_install_editable_from_hg(script, tmpdir):
332 333 334
    """Test cloning from Mercurial."""
    pkg_path = _create_test_package(script, name='testpackage', vcs='hg')
    args = ['install', '-e', 'hg+%s#egg=testpackage' % path_to_url(pkg_path)]
C
Chris Hunt 已提交
335
    result = script.pip(*args)
336
    result.assert_installed('testpackage', with_files=['.hg'])
337

338

339
@need_mercurial
340
def test_vcs_url_final_slash_normalization(script, tmpdir):
341 342 343
    """
    Test that presence or absence of final slash in VCS URL is normalized.
    """
344 345
    pkg_path = _create_test_package(script, name='testpackage', vcs='hg')
    args = ['install', '-e', 'hg+%s/#egg=testpackage' % path_to_url(pkg_path)]
C
Chris Hunt 已提交
346
    result = script.pip(*args)
347
    result.assert_installed('testpackage', with_files=['.hg'])
348

349

350
@need_bzr
351
def test_install_editable_from_bazaar(script, tmpdir):
352 353 354
    """Test checking out from Bazaar."""
    pkg_path = _create_test_package(script, name='testpackage', vcs='bazaar')
    args = ['install', '-e', 'bzr+%s/#egg=testpackage' % path_to_url(pkg_path)]
C
Chris Hunt 已提交
355
    result = script.pip(*args)
356
    result.assert_installed('testpackage', with_files=['.bzr'])
F
Francesco 已提交
357

358

359
@pytest.mark.network
360
@need_bzr
361
def test_vcs_url_urlquote_normalization(script, tmpdir):
362 363 364
    """
    Test that urlquoted characters are normalized for repo URL comparison.
    """
365
    script.pip(
366 367 368 369 370
        'install', '-e',
        '%s/#egg=django-wikiapp' %
        local_checkout(
            'bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp'
            '/release-0.1',
371
            tmpdir.joinpath("cache"),
372 373
        ),
    )
374

375

376
def test_basic_install_from_local_directory(script, data):
377 378 379
    """
    Test installing from a local directory.
    """
380
    to_install = data.packages.joinpath("FSPkg")
D
Donald Stufft 已提交
381
    result = script.pip('install', to_install, expect_error=False)
D
Donald Stufft 已提交
382
    fspkg_folder = script.site_packages / 'fspkg'
383
    egg_info_folder = (
D
Donald Stufft 已提交
384
        script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
385
    )
386 387
    assert fspkg_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)
388

389

390
def test_basic_install_relative_directory(script, data):
391
    """
392
    Test installing a requirement using a relative path.
393
    """
394
    egg_info_file = (
395 396 397 398 399
        script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
    )
    egg_link_file = (
        script.site_packages / 'FSPkg.egg-link'
    )
400 401 402
    package_folder = script.site_packages / 'fspkg'

    # Compute relative install path to FSPkg from scratch path.
403
    full_rel_path = data.packages.joinpath('FSPkg') - script.scratch_path
404 405 406
    full_rel_url = (
        'file:' + full_rel_path.replace(os.path.sep, '/') + '#egg=FSPkg'
    )
407
    embedded_rel_path = script.scratch_path.joinpath(full_rel_path)
408 409 410

    # For each relative path, install as either editable or not using either
    # URLs with egg links or not.
411
    for req_path in (full_rel_path, full_rel_url, embedded_rel_path):
412 413 414 415 416
        # Regular install.
        result = script.pip('install', req_path,
                            cwd=script.scratch_path)
        assert egg_info_file in result.files_created, str(result)
        assert package_folder in result.files_created, str(result)
417 418
        script.pip('uninstall', '-y', 'fspkg')

419 420 421 422 423
        # Editable install.
        result = script.pip('install', '-e' + req_path,
                            cwd=script.scratch_path)
        assert egg_link_file in result.files_created, str(result)
        script.pip('uninstall', '-y', 'fspkg')
424 425


426 427 428 429 430 431 432 433
def test_install_quiet(script, data):
    """
    Test that install -q is actually quiet.
    """
    # Apparently if pip install -q is not actually quiet, then it breaks
    # everything. See:
    #   https://github.com/pypa/pip/issues/3418
    #   https://github.com/docker-library/python/issues/83
434
    to_install = data.packages.joinpath("FSPkg")
435
    result = script.pip('install', '-qqq', to_install, expect_error=False)
436 437 438 439
    assert result.stdout == ""
    assert result.stderr == ""


440 441 442 443 444 445 446 447 448 449 450
def test_hashed_install_success(script, data, tmpdir):
    """
    Test that installing various sorts of requirements with correct hashes
    works.

    Test file URLs and index packages (which become HTTP URLs behind the
    scenes).

    """
    file_url = path_to_url(
        (data.packages / 'simple-1.0.tar.gz').abspath)
E
Erik Rose 已提交
451 452 453 454 455 456 457
    with requirements_file(
            'simple2==1.0 --hash=sha256:9336af72ca661e6336eb87bc7de3e8844d853e'
            '3848c2b9bbd2e8bf01db88c2c7\n'
            '{simple} --hash=sha256:393043e672415891885c9a2a0929b1af95fb866d6c'
            'a016b42d2e6ce53619b653'.format(simple=file_url),
            tmpdir) as reqs_file:
        script.pip_install_local('-r', reqs_file.abspath, expect_error=False)
458 459


460
def test_hashed_install_failure(script, tmpdir):
461 462 463 464 465 466 467
    """Test that wrong hashes stop installation.

    This makes sure prepare_files() is called in the course of installation
    and so has the opportunity to halt if hashes are wrong. Checks on various
    kinds of hashes are in test_req.py.

    """
E
Erik Rose 已提交
468 469
    with requirements_file('simple2==1.0 --hash=sha256:9336af72ca661e6336eb87b'
                           'c7de3e8844d853e3848c2b9bbd2e8bf01db88c2c\n',
470 471 472 473 474 475 476
                           tmpdir) as reqs_file:
        result = script.pip_install_local('-r',
                                          reqs_file.abspath,
                                          expect_error=True)
    assert len(result.files_created) == 0


477 478
def test_install_from_local_directory_with_symlinks_to_directories(
        script, data):
479 480 481
    """
    Test installing from a local directory containing symlinks to directories.
    """
482
    to_install = data.packages.joinpath("symlinks")
483
    result = script.pip('install', to_install, expect_error=False)
D
Donald Stufft 已提交
484
    pkg_folder = script.site_packages / 'symlinks'
485
    egg_info_folder = (
D
Donald Stufft 已提交
486
        script.site_packages / 'symlinks-0.1.dev0-py%s.egg-info' % pyversion
487
    )
488 489 490 491
    assert pkg_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)


492
def test_install_from_local_directory_with_no_setup_py(script, data):
493 494 495
    """
    Test installing from a local directory with no 'setup.py'.
    """
496
    result = script.pip('install', data.root, expect_error=True)
497
    assert not result.files_created
498 499
    assert "is not installable." in result.stderr
    assert "Neither 'setup.py' nor 'pyproject.toml' found." in result.stderr
500

501

502 503
def test_editable_install__local_dir_no_setup_py(
        script, data, deprecated_python):
504
    """
505
    Test installing in editable mode from a local directory with no setup.py.
506
    """
507
    result = script.pip('install', '-e', data.root, expect_error=True)
508
    assert not result.files_created
509 510 511 512 513

    msg = result.stderr
    if deprecated_python:
        assert 'File "setup.py" not found. ' in msg
    else:
514
        assert msg.startswith('ERROR: File "setup.py" not found. ')
515 516 517 518 519 520 521 522 523
    assert 'pyproject.toml' not in msg


def test_editable_install__local_dir_no_setup_py_with_pyproject(
        script, deprecated_python):
    """
    Test installing in editable mode from a local directory with no setup.py
    but that does have pyproject.toml.
    """
524 525 526
    local_dir = script.scratch_path.joinpath('temp').mkdir()
    pyproject_path = local_dir.joinpath('pyproject.toml')
    pyproject_path.write_text('')
527 528 529 530 531 532 533 534

    result = script.pip('install', '-e', local_dir, expect_error=True)
    assert not result.files_created

    msg = result.stderr
    if deprecated_python:
        assert 'File "setup.py" not found. ' in msg
    else:
535
        assert msg.startswith('ERROR: File "setup.py" not found. ')
536
    assert 'A "pyproject.toml" file was found' in msg
537 538


539
@pytest.mark.skipif("sys.version_info >= (3,4)")
540
@pytest.mark.xfail
541
def test_install_argparse_shadowed(script):
542
    # When argparse is in the stdlib, we support installing it
J
Jakub Wilk 已提交
543
    # even though that's pretty useless because older packages did need to
544 545 546 547 548 549 550 551 552
    # depend on it, and not having its metadata will cause pkg_resources
    # requirements checks to fail // trigger easy-install, both of which are
    # bad.
    # XXX: Note, this test hits the outside-environment check, not the
    # in-stdlib check, because our tests run in virtualenvs...
    result = script.pip('install', 'argparse>=1.4')
    assert "Not uninstalling argparse" in result.stdout


M
Miro Hrončok 已提交
553
@pytest.mark.network
554
@pytest.mark.skipif("sys.version_info < (3,4)")
555
def test_upgrade_argparse_shadowed(script):
556 557 558 559 560 561 562
    # If argparse is installed - even if shadowed for imported - we support
    # upgrading it and properly remove the older versions files.
    script.pip('install', 'argparse==1.3')
    result = script.pip('install', 'argparse>=1.4')
    assert "Not uninstalling argparse" not in result.stdout


563
def test_install_curdir(script, data):
564 565 566
    """
    Test installing current directory ('.').
    """
567
    run_from = data.packages.joinpath("FSPkg")
568 569 570 571
    # Python 2.4 Windows balks if this exists already
    egg_info = join(run_from, "FSPkg.egg-info")
    if os.path.isdir(egg_info):
        rmtree(egg_info)
D
Donald Stufft 已提交
572
    result = script.pip('install', curdir, cwd=run_from, expect_error=False)
D
Donald Stufft 已提交
573
    fspkg_folder = script.site_packages / 'fspkg'
574
    egg_info_folder = (
D
Donald Stufft 已提交
575
        script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
576
    )
577 578
    assert fspkg_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)
579

580

581
def test_install_pardir(script, data):
582 583 584
    """
    Test installing parent directory ('..').
    """
585
    run_from = data.packages.joinpath("FSPkg", "fspkg")
D
Donald Stufft 已提交
586
    result = script.pip('install', pardir, cwd=run_from, expect_error=False)
D
Donald Stufft 已提交
587
    fspkg_folder = script.site_packages / 'fspkg'
588
    egg_info_folder = (
D
Donald Stufft 已提交
589
        script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
590
    )
591 592
    assert fspkg_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)
593 594


595
@pytest.mark.network
596
def test_install_global_option(script):
597
    """
598 599
    Test using global distutils options.
    (In particular those that disable the actual install action)
600
    """
601 602
    result = script.pip(
        'install', '--global-option=--version', "INITools==0.1",
603
        expect_stderr=True)
604
    assert 'INITools==0.1\n' in result.stdout
605 606


607
def test_install_with_hacked_egg_info(script, data):
608 609 610
    """
    test installing a package which defines its own egg_info class
    """
611
    run_from = data.packages.joinpath("HackedEggInfo")
D
Donald Stufft 已提交
612
    result = script.pip('install', '.', cwd=run_from)
613
    assert 'Successfully installed hackedegginfo-0.0.0\n' in result.stdout
614 615


616
@pytest.mark.network
617
def test_install_using_install_option_and_editable(script, tmpdir):
618 619 620
    """
    Test installing a tool using -e and --install-option
    """
H
Hugo Lopes Tavares 已提交
621
    folder = 'script_folder'
622
    script.scratch_path.joinpath(folder).mkdir()
623
    url = 'git+git://github.com/pypa/pip-test-package'
624
    result = script.pip(
625
        'install', '-e', '%s#egg=pip-test-package' %
626
        local_checkout(url, tmpdir.joinpath("cache")),
627 628
        '--install-option=--script-dir=%s' % folder,
        expect_stderr=True)
629 630 631
    script_file = (
        script.venv / 'src' / 'pip-test-package' /
        folder / 'pip-test-package' + script.exe
632
    )
633
    assert script_file in result.files_created
634 635


636
@pytest.mark.network
637
@need_mercurial
638
def test_install_global_option_using_editable(script, tmpdir):
639 640 641
    """
    Test using global distutils options, but in an editable installation
    """
C
Carl Meyer 已提交
642
    url = 'hg+http://bitbucket.org/runeh/anyjson'
643 644
    result = script.pip(
        'install', '--global-option=--version', '-e',
645
        '%s@0.2.5#egg=anyjson' % local_checkout(url, tmpdir.joinpath("cache")),
646
        expect_stderr=True)
647
    assert 'Successfully installed anyjson' in result.stdout
648 649


650
@pytest.mark.network
651
def test_install_package_with_same_name_in_curdir(script):
652 653 654
    """
    Test installing a package with the same name of a local folder
    """
655
    script.scratch_path.joinpath("mock==0.6").mkdir()
D
Donald Stufft 已提交
656 657
    result = script.pip('install', 'mock==0.6')
    egg_folder = script.site_packages / 'mock-0.6.0-py%s.egg-info' % pyversion
658 659 660 661 662 663 664 665 666
    assert egg_folder in result.files_created, str(result)


mock100_setup_py = textwrap.dedent('''\
                        from setuptools import setup
                        setup(name='mock',
                              version='100.1')''')


667
def test_install_folder_using_dot_slash(script):
668 669 670
    """
    Test installing a folder using pip install ./foldername
    """
671
    script.scratch_path.joinpath("mock").mkdir()
D
Donald Stufft 已提交
672
    pkg_path = script.scratch_path / 'mock'
673
    pkg_path.joinpath("setup.py").write_text(mock100_setup_py)
D
Donald Stufft 已提交
674 675
    result = script.pip('install', './mock')
    egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion
676 677 678
    assert egg_folder in result.files_created, str(result)


679
def test_install_folder_using_slash_in_the_end(script):
680 681
    r"""
    Test installing a folder using pip install foldername/ or foldername\
682
    """
683
    script.scratch_path.joinpath("mock").mkdir()
D
Donald Stufft 已提交
684
    pkg_path = script.scratch_path / 'mock'
685
    pkg_path.joinpath("setup.py").write_text(mock100_setup_py)
D
Donald Stufft 已提交
686 687
    result = script.pip('install', 'mock' + os.path.sep)
    egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion
688 689 690
    assert egg_folder in result.files_created, str(result)


691
def test_install_folder_using_relative_path(script):
692 693 694
    """
    Test installing a folder using pip install folder1/folder2
    """
695 696
    script.scratch_path.joinpath("initools").mkdir()
    script.scratch_path.joinpath("initools", "mock").mkdir()
D
Donald Stufft 已提交
697
    pkg_path = script.scratch_path / 'initools' / 'mock'
698
    pkg_path.joinpath("setup.py").write_text(mock100_setup_py)
D
Donald Stufft 已提交
699
    result = script.pip('install', Path('initools') / 'mock')
D
Donald Stufft 已提交
700
    egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion
701
    assert egg_folder in result.files_created, str(result)
702

703

704
@pytest.mark.network
705
def test_install_package_which_contains_dev_in_name(script):
706
    """
J
Jon Dufresne 已提交
707
    Test installing package from PyPI which contains 'dev' in name
708
    """
D
Donald Stufft 已提交
709
    result = script.pip('install', 'django-devserver==0.0.4')
D
Donald Stufft 已提交
710
    devserver_folder = script.site_packages / 'devserver'
711
    egg_info_folder = (
D
Donald Stufft 已提交
712 713
        script.site_packages / 'django_devserver-0.0.4-py%s.egg-info' %
        pyversion
714
    )
715 716
    assert devserver_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)
717

718

719
def test_install_package_with_target(script):
720 721 722
    """
    Test installing a package using pip install --target
    """
D
Donald Stufft 已提交
723
    target_dir = script.scratch_path / 'target'
724 725
    result = script.pip_install_local('-t', target_dir, "simple==1.0")
    assert Path('scratch') / 'target' / 'simple' in result.files_created, (
726 727
        str(result)
    )
728

729
    # Test repeated call without --upgrade, no files should have changed
730 731 732
    result = script.pip_install_local(
        '-t', target_dir, "simple==1.0", expect_stderr=True,
    )
733
    assert not Path('scratch') / 'target' / 'simple' in result.files_updated
734 735

    # Test upgrade call, check that new version is installed
736 737 738
    result = script.pip_install_local('--upgrade', '-t',
                                      target_dir, "simple==2.0")
    assert Path('scratch') / 'target' / 'simple' in result.files_updated, (
739 740
        str(result)
    )
J
Jon Wayne Parrott 已提交
741
    egg_folder = (
742
        Path('scratch') / 'target' / 'simple-2.0-py%s.egg-info' % pyversion)
J
Jon Wayne Parrott 已提交
743
    assert egg_folder in result.files_created, (
744 745 746
        str(result)
    )

747
    # Test install and upgrade of single-module package
748 749 750
    result = script.pip_install_local('-t', target_dir, 'singlemodule==0.0.0')
    singlemodule_py = Path('scratch') / 'target' / 'singlemodule.py'
    assert singlemodule_py in result.files_created, str(result)
751

752 753 754
    result = script.pip_install_local('-t', target_dir, 'singlemodule==0.0.1',
                                      '--upgrade')
    assert singlemodule_py in result.files_updated, str(result)
755

756

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
def test_install_nonlocal_compatible_wheel(script, data):
    target_dir = script.scratch_path / 'target'

    # Test install with --target
    result = script.pip(
        'install',
        '-t', target_dir,
        '--no-index', '--find-links', data.find_links,
        '--only-binary=:all:',
        '--python', '3',
        '--platform', 'fakeplat',
        '--abi', 'fakeabi',
        'simplewheel',
    )
    assert result.returncode == SUCCESS

    distinfo = Path('scratch') / 'target' / 'simplewheel-2.0-1.dist-info'
    assert distinfo in result.files_created

    # Test install without --target
    result = script.pip(
        'install',
        '--no-index', '--find-links', data.find_links,
        '--only-binary=:all:',
        '--python', '3',
        '--platform', 'fakeplat',
        '--abi', 'fakeabi',
        'simplewheel',
        expect_error=True
    )
    assert result.returncode == ERROR


def test_install_nonlocal_compatible_wheel_path(script, data):
    target_dir = script.scratch_path / 'target'

    # Test a full path requirement
    result = script.pip(
        'install',
        '-t', target_dir,
        '--no-index',
        '--only-binary=:all:',
        Path(data.packages) / 'simplewheel-2.0-py3-fakeabi-fakeplat.whl'
    )
    assert result.returncode == SUCCESS

    distinfo = Path('scratch') / 'target' / 'simplewheel-2.0.dist-info'
    assert distinfo in result.files_created

    # Test a full path requirement (without --target)
    result = script.pip(
        'install',
        '--no-index',
        '--only-binary=:all:',
        Path(data.packages) / 'simplewheel-2.0-py3-fakeabi-fakeplat.whl',
        expect_error=True
    )
    assert result.returncode == ERROR


817
def test_install_with_target_and_scripts_no_warning(script, with_wheel):
818 819 820 821 822 823 824
    """
    Test that installing with --target does not trigger the "script not
    in PATH" warning (issue #5201)
    """
    target_dir = script.scratch_path / 'target'
    pkga_path = script.scratch_path / 'pkga'
    pkga_path.mkdir()
825
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
826 827 828 829 830 831 832 833 834
        from setuptools import setup
        setup(name='pkga',
              version='0.1',
              py_modules=["pkga"],
              entry_points={
                  'console_scripts': ['pkga=pkga:main']
              }
        )
    """))
835
    pkga_path.joinpath("pkga.py").write_text(textwrap.dedent("""
836 837 838 839 840 841 842 843 844
        def main(): pass
    """))
    result = script.pip('install', '--target', target_dir, pkga_path)
    # This assertion isn't actually needed, if we get the script warning
    # the script.pip() call will fail with "stderr not expected". But we
    # leave the assertion to make the intention of the code clearer.
    assert "--no-warn-script-location" not in result.stderr, str(result)


845
def test_install_package_with_root(script, data):
846 847 848
    """
    Test installing a package using pip install --root
    """
D
Donald Stufft 已提交
849
    root_dir = script.scratch_path / 'root'
850 851 852 853 854 855 856 857 858
    result = script.pip(
        'install', '--root', root_dir, '-f', data.find_links, '--no-index',
        'simple==1.0',
    )
    normal_install_path = (
        script.base_path / script.site_packages / 'simple-1.0-py%s.egg-info' %
        pyversion
    )
    # use distutils to change the root exactly how the --root option does it
M
Marcus Smith 已提交
859
    from distutils.util import change_root
860 861 862 863
    root_path = change_root(
        os.path.join(script.scratch, 'root'),
        normal_install_path
    )
M
Marcus Smith 已提交
864
    assert root_path in result.files_created, str(result)
865

866 867 868 869
    # Should show find-links location in output
    assert "Looking in indexes: " not in result.stdout
    assert "Looking in links: " in result.stdout

870

871 872 873 874 875 876 877
def test_install_package_with_prefix(script, data):
    """
    Test installing a package using pip install --prefix
    """
    prefix_path = script.scratch_path / 'prefix'
    result = script.pip(
        'install', '--prefix', prefix_path, '-f', data.find_links,
878
        '--no-binary', 'simple', '--no-index', 'simple==1.0',
879 880
    )

881
    rel_prefix_path = script.scratch / 'prefix'
882
    install_path = (
883
        distutils.sysconfig.get_python_lib(prefix=rel_prefix_path) /
A
Anthony Sottile 已提交
884
        'simple-1.0-py{}.egg-info'.format(pyversion)
885 886 887 888
    )
    assert install_path in result.files_created, str(result)


889 890 891 892
def test_install_editable_with_prefix(script):
    # make a dummy project
    pkga_path = script.scratch_path / 'pkga'
    pkga_path.mkdir()
893
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
894 895 896 897 898
        from setuptools import setup
        setup(name='pkga',
              version='0.1')
    """))

899 900
    if hasattr(sys, "pypy_version_info"):
        site_packages = os.path.join(
A
Anthony Sottile 已提交
901
            'prefix', 'lib', 'python{}'.format(pyversion), 'site-packages')
902 903
    else:
        site_packages = distutils.sysconfig.get_python_lib(prefix='prefix')
904 905 906

    # make sure target path is in PYTHONPATH
    pythonpath = script.scratch_path / site_packages
907
    pythonpath.mkdir(parents=True)
908 909 910 911 912 913 914 915 916 917 918 919
    script.environ["PYTHONPATH"] = pythonpath

    # install pkga package into the absolute prefix directory
    prefix_path = script.scratch_path / 'prefix'
    result = script.pip(
        'install', '--editable', pkga_path, '--prefix', prefix_path)

    # assert pkga is installed at correct location
    install_path = script.scratch / site_packages / 'pkga.egg-link'
    assert install_path in result.files_created, str(result)


920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
def test_install_package_conflict_prefix_and_user(script, data):
    """
    Test installing a package using pip install --prefix --user errors out
    """
    prefix_path = script.scratch_path / 'prefix'
    result = script.pip(
        'install', '-f', data.find_links, '--no-index', '--user',
        '--prefix', prefix_path, 'simple==1.0',
        expect_error=True, quiet=True,
    )
    assert (
        "Can not combine '--user' and '--prefix'" in result.stderr
    )


935
def test_install_package_that_emits_unicode(script, data):
936
    """
937
    Install a package with a setup.py that emits UTF-8 output and then fails.
938

939
    Refs https://github.com/pypa/pip/issues/326
940
    """
941
    to_install = data.packages.joinpath("BrokenEmitsUTF8")
942 943 944 945
    result = script.pip(
        'install', to_install, expect_error=True, expect_temp=True, quiet=True,
    )
    assert (
946
        'FakeError: this package designed to fail on install' in result.stderr
947
    ), 'stderr: {}'.format(result.stderr)
948
    assert 'UnicodeDecodeError' not in result.stderr
949
    assert 'UnicodeDecodeError' not in result.stdout
950

951

952 953
def test_install_package_with_utf8_setup(script, data):
    """Install a package with a setup.py that declares a utf-8 encoding."""
954
    to_install = data.packages.joinpath("SetupPyUTF8")
955 956
    script.pip('install', to_install)

957

958 959
def test_install_package_with_latin1_setup(script, data):
    """Install a package with a setup.py that declares a latin-1 encoding."""
960
    to_install = data.packages.joinpath("SetupPyLatin1")
961
    script.pip('install', to_install)
962

963

964
def test_url_req_case_mismatch_no_index(script, data):
965
    """
966 967 968
    tar ball url requirements (with no egg fragment), that happen to have upper
    case project names, should be considered equal to later requirements that
    reference the project name using lower case.
969

970
    tests/data/packages contains Upper-1.0.tar.gz and Upper-2.0.tar.gz
971 972
    'requiresupper' has install_requires = ['upper']
    """
973
    Upper = '/'.join((data.find_links, 'Upper-1.0.tar.gz'))
974 975 976
    result = script.pip(
        'install', '--no-index', '-f', data.find_links, Upper, 'requiresupper'
    )
977

978
    # only Upper-1.0.tar.gz should get installed.
D
Donald Stufft 已提交
979
    egg_folder = script.site_packages / 'Upper-1.0-py%s.egg-info' % pyversion
980
    assert egg_folder in result.files_created, str(result)
D
Donald Stufft 已提交
981
    egg_folder = script.site_packages / 'Upper-2.0-py%s.egg-info' % pyversion
982
    assert egg_folder not in result.files_created, str(result)
983 984


985 986 987 988 989 990
def test_url_req_case_mismatch_file_index(script, data):
    """
    tar ball url requirements (with no egg fragment), that happen to have upper
    case project names, should be considered equal to later requirements that
    reference the project name using lower case.

991
    tests/data/packages3 contains Dinner-1.0.tar.gz and Dinner-2.0.tar.gz
992 993 994 995 996 997 998 999
    'requiredinner' has install_requires = ['dinner']

    This test is similar to test_url_req_case_mismatch_no_index; that test
    tests behaviour when using "--no-index -f", while this one does the same
    test when using "--index-url". Unfortunately this requires a different
    set of packages as it requires a prepared index.html file and
    subdirectory-per-package structure.
    """
1000
    Dinner = '/'.join((data.find_links3, 'dinner', 'Dinner-1.0.tar.gz'))
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
    result = script.pip(
        'install', '--index-url', data.find_links3, Dinner, 'requiredinner'
    )

    # only Upper-1.0.tar.gz should get installed.
    egg_folder = script.site_packages / 'Dinner-1.0-py%s.egg-info' % pyversion
    assert egg_folder in result.files_created, str(result)
    egg_folder = script.site_packages / 'Dinner-2.0-py%s.egg-info' % pyversion
    assert egg_folder not in result.files_created, str(result)


def test_url_incorrect_case_no_index(script, data):
    """
    Same as test_url_req_case_mismatch_no_index, except testing for the case
    where the incorrect case is given in the name of the package to install
    rather than in a requirements file.
    """
    result = script.pip(
        'install', '--no-index', '-f', data.find_links, "upper",
    )

    # only Upper-2.0.tar.gz should get installed.
    egg_folder = script.site_packages / 'Upper-1.0-py%s.egg-info' % pyversion
    assert egg_folder not in result.files_created, str(result)
    egg_folder = script.site_packages / 'Upper-2.0-py%s.egg-info' % pyversion
    assert egg_folder in result.files_created, str(result)


def test_url_incorrect_case_file_index(script, data):
    """
    Same as test_url_req_case_mismatch_file_index, except testing for the case
    where the incorrect case is given in the name of the package to install
    rather than in a requirements file.
    """
    result = script.pip(
        'install', '--index-url', data.find_links3, "dinner",
1037
        expect_stderr=True,
1038 1039 1040 1041 1042 1043 1044 1045
    )

    # only Upper-2.0.tar.gz should get installed.
    egg_folder = script.site_packages / 'Dinner-1.0-py%s.egg-info' % pyversion
    assert egg_folder not in result.files_created, str(result)
    egg_folder = script.site_packages / 'Dinner-2.0-py%s.egg-info' % pyversion
    assert egg_folder in result.files_created, str(result)

1046 1047 1048 1049
    # Should show index-url location in output
    assert "Looking in indexes: " in result.stdout
    assert "Looking in links: " not in result.stdout

1050

1051
@pytest.mark.network
1052 1053 1054 1055 1056
def test_compiles_pyc(script):
    """
    Test installing with --compile on
    """
    del script.environ["PYTHONDONTWRITEBYTECODE"]
1057
    script.pip("install", "--compile", "--no-binary=:all:", "INITools==0.2")
1058 1059 1060 1061

    # There are many locations for the __init__.pyc file so attempt to find
    #   any of them
    exists = [
D
Donald Stufft 已提交
1062
        os.path.exists(script.site_packages_path / "initools/__init__.pyc"),
1063 1064 1065
    ]

    exists += glob.glob(
D
Donald Stufft 已提交
1066
        script.site_packages_path / "initools/__pycache__/__init__*.pyc"
1067 1068 1069 1070 1071
    )

    assert any(exists)


1072
@pytest.mark.network
1073
def test_no_compiles_pyc(script):
1074 1075 1076 1077
    """
    Test installing from wheel with --compile on
    """
    del script.environ["PYTHONDONTWRITEBYTECODE"]
1078
    script.pip("install", "--no-compile", "--no-binary=:all:", "INITools==0.2")
1079 1080 1081 1082

    # There are many locations for the __init__.pyc file so attempt to find
    #   any of them
    exists = [
D
Donald Stufft 已提交
1083
        os.path.exists(script.site_packages_path / "initools/__init__.pyc"),
1084 1085 1086
    ]

    exists += glob.glob(
D
Donald Stufft 已提交
1087
        script.site_packages_path / "initools/__pycache__/__init__*.pyc"
1088 1089 1090
    )

    assert not any(exists)
1091 1092 1093


def test_install_upgrade_editable_depending_on_other_editable(script):
1094
    script.scratch_path.joinpath("pkga").mkdir()
1095
    pkga_path = script.scratch_path / 'pkga'
1096
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
1097 1098 1099 1100 1101
        from setuptools import setup
        setup(name='pkga',
              version='0.1')
    """))
    script.pip('install', '--editable', pkga_path)
X
Xavier Fernandez 已提交
1102 1103
    result = script.pip('list', '--format=freeze')
    assert "pkga==0.1" in result.stdout
1104

1105
    script.scratch_path.joinpath("pkgb").mkdir()
1106
    pkgb_path = script.scratch_path / 'pkgb'
1107
    pkgb_path.joinpath("setup.py").write_text(textwrap.dedent("""
1108 1109 1110 1111 1112
        from setuptools import setup
        setup(name='pkgb',
              version='0.1',
              install_requires=['pkga'])
    """))
X
Xavier Fernandez 已提交
1113 1114 1115
    script.pip('install', '--upgrade', '--editable', pkgb_path, '--no-index')
    result = script.pip('list', '--format=freeze')
    assert "pkgb==0.1" in result.stdout
1116

1117 1118

def test_install_subprocess_output_handling(script, data):
1119
    args = ['install', data.src.joinpath('chattymodule')]
1120 1121 1122 1123 1124 1125 1126 1127 1128

    # Regular install should not show output from the chatty setup.py
    result = script.pip(*args)
    assert 0 == result.stdout.count("HELLO FROM CHATTYMODULE")
    script.pip("uninstall", "-y", "chattymodule")

    # With --verbose we should show the output.
    # Only count examples with sys.argv[1] == egg_info, because we call
    # setup.py multiple times, which should not count as duplicate output.
1129 1130
    result = script.pip(*(args + ["--verbose"]), expect_stderr=True)
    assert 1 == result.stderr.count("HELLO FROM CHATTYMODULE egg_info")
1131 1132 1133 1134 1135 1136
    script.pip("uninstall", "-y", "chattymodule")

    # If the install fails, then we *should* show the output... but only once,
    # even if --verbose is given.
    result = script.pip(*(args + ["--global-option=--fail"]),
                        expect_error=True)
1137
    assert 1 == result.stderr.count("I DIE, I DIE")
1138 1139 1140

    result = script.pip(*(args + ["--global-option=--fail", "--verbose"]),
                        expect_error=True)
1141
    assert 1 == result.stderr.count("I DIE, I DIE")
1142

1143 1144 1145

def test_install_log(script, data, tmpdir):
    # test that verbose logs go to "--log" file
1146
    f = tmpdir.joinpath("log.txt")
1147
    args = ['--log=%s' % f,
1148
            'install', data.src.joinpath('chattymodule')]
1149 1150 1151 1152 1153 1154
    result = script.pip(*args)
    assert 0 == result.stdout.count("HELLO FROM CHATTYMODULE")
    with open(f, 'r') as fp:
        # one from egg_info, one from install
        assert 2 == fp.read().count("HELLO FROM CHATTYMODULE")

1155 1156

def test_install_topological_sort(script, data):
X
Xavier Fernandez 已提交
1157
    args = ['install', 'TopoRequires4', '--no-index', '-f', data.packages]
1158 1159 1160 1161
    res = str(script.pip(*args, expect_error=False))
    order1 = 'TopoRequires, TopoRequires2, TopoRequires3, TopoRequires4'
    order2 = 'TopoRequires, TopoRequires3, TopoRequires2, TopoRequires4'
    assert order1 in res or order2 in res, res
1162 1163


1164 1165
def test_install_wheel_broken(script, with_wheel):
    res = script.pip_install_local('wheelbroken', expect_stderr=True)
1166
    assert "Successfully installed wheelbroken-0.1" in str(res), str(res)
1167 1168


1169 1170
def test_cleanup_after_failed_wheel(script, with_wheel):
    res = script.pip_install_local('wheelbrokenafter', expect_stderr=True)
1171 1172
    # One of the effects of not cleaning up is broken scripts:
    script_py = script.bin_path / "script.py"
1173
    assert script_py.exists(), script_py
1174 1175 1176 1177 1178 1179
    shebang = open(script_py, 'r').readline().strip()
    assert shebang != '#!python', shebang
    # OK, assert that we *said* we were cleaning up:
    assert "Running setup.py clean for wheelbrokenafter" in str(res), str(res)


1180
def test_install_builds_wheels(script, data, with_wheel):
1181 1182 1183 1184 1185 1186
    # We need to use a subprocess to get the right value on Windows.
    res = script.run('python', '-c', (
        'from pip._internal.utils import appdirs; '
        'print(appdirs.user_cache_dir("pip"))'
    ))
    wheels_cache = os.path.join(res.stdout.rstrip('\n'), 'wheels')
1187 1188 1189
    # NB This incidentally tests a local tree + tarball inputs
    # see test_install_editable_from_git_autobuild_wheel for editable
    # vcs coverage.
1190
    to_install = data.packages.joinpath('requires_wheelbroken_upper')
1191
    res = script.pip(
1192
        'install', '--no-index', '-f', data.find_links,
1193 1194 1195 1196 1197 1198
        to_install, expect_stderr=True)
    expected = ("Successfully installed requires-wheelbroken-upper-0"
                " upper-2.0 wheelbroken-0.1")
    # Must have installed it all
    assert expected in str(res), str(res)
    wheels = []
1199
    for top, dirs, files in os.walk(wheels_cache):
1200 1201
        wheels.extend(files)
    # and built wheels for upper and wheelbroken
P
Paul Moore 已提交
1202 1203
    assert "Building wheel for upper" in str(res), str(res)
    assert "Building wheel for wheelb" in str(res), str(res)
T
Thomas Kluyver 已提交
1204
    # Wheels are built for local directories, but not cached.
P
Paul Moore 已提交
1205
    assert "Building wheel for requir" in str(res), str(res)
1206 1207 1208 1209 1210
    # wheelbroken has to run install
    # into the cache
    assert wheels != [], str(res)
    # and installed from the wheel
    assert "Running setup.py install for upper" not in str(res), str(res)
T
Thomas Kluyver 已提交
1211 1212
    # Wheels are built for local directories, but not cached.
    assert "Running setup.py install for requir" not in str(res), str(res)
1213 1214
    # wheelbroken has to run install
    assert "Running setup.py install for wheelb" in str(res), str(res)
1215 1216
    # We want to make sure we used the correct implementation tag
    assert wheels == [
A
Anthony Sottile 已提交
1217
        "Upper-2.0-{}-none-any.whl".format(pep425tags.implementation_tag),
1218
    ]
1219 1220


1221
def test_install_no_binary_disables_building_wheels(script, data, with_wheel):
1222
    to_install = data.packages.joinpath('requires_wheelbroken_upper')
1223 1224 1225 1226 1227 1228 1229 1230
    res = script.pip(
        'install', '--no-index', '--no-binary=upper', '-f', data.find_links,
        to_install, expect_stderr=True)
    expected = ("Successfully installed requires-wheelbroken-upper-0"
                " upper-2.0 wheelbroken-0.1")
    # Must have installed it all
    assert expected in str(res), str(res)
    # and built wheels for wheelbroken only
P
Paul Moore 已提交
1231
    assert "Building wheel for wheelb" in str(res), str(res)
1232
    # Wheels are built for local directories, but not cached across runs
P
Paul Moore 已提交
1233
    assert "Building wheel for requir" in str(res), str(res)
1234
    # Don't build wheel for upper which was blacklisted
P
Paul Moore 已提交
1235
    assert "Building wheel for upper" not in str(res), str(res)
1236
    # Wheels are built for local directories, but not cached across runs
1237
    assert "Running setup.py install for requir" not in str(res), str(res)
1238
    # And these two fell back to sdist based installed.
1239 1240
    assert "Running setup.py install for wheelb" in str(res), str(res)
    assert "Running setup.py install for upper" in str(res), str(res)
1241 1242


1243
def test_install_no_binary_disables_cached_wheels(script, data, with_wheel):
1244 1245
    # Seed the cache
    script.pip(
1246
        'install', '--no-index', '-f', data.find_links,
1247 1248 1249 1250 1251 1252 1253
        'upper')
    script.pip('uninstall', 'upper', '-y')
    res = script.pip(
        'install', '--no-index', '--no-binary=:all:', '-f', data.find_links,
        'upper', expect_stderr=True)
    assert "Successfully installed upper-2.0" in str(res), str(res)
    # No wheel building for upper, which was blacklisted
P
Paul Moore 已提交
1254
    assert "Building wheel for upper" not in str(res), str(res)
1255 1256
    # Must have used source, not a cached wheel to install upper.
    assert "Running setup.py install for upper" in str(res), str(res)
1257 1258 1259


def test_install_editable_with_wrong_egg_name(script):
1260
    script.scratch_path.joinpath("pkga").mkdir()
1261
    pkga_path = script.scratch_path / 'pkga'
1262
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
1263 1264 1265 1266 1267
        from setuptools import setup
        setup(name='pkga',
              version='0.1')
    """))
    result = script.pip(
C
Chris Hunt 已提交
1268 1269
        'install', '--editable', 'file://%s#egg=pkgb' % pkga_path
    )
P
Paul Moore 已提交
1270
    assert ("Generating metadata for package pkgb produced metadata "
1271 1272 1273
            "for project name pkga. Fix your #egg=pkgb "
            "fragments.") in result.stderr
    assert "Successfully installed pkga" in str(result), str(result)
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291


def test_install_tar_xz(script, data):
    try:
        import lzma  # noqa
    except ImportError:
        pytest.skip("No lzma support")
    res = script.pip('install', data.packages / 'singlemodule-0.0.1.tar.xz')
    assert "Successfully installed singlemodule-0.0.1" in res.stdout, res


def test_install_tar_lzma(script, data):
    try:
        import lzma  # noqa
    except ImportError:
        pytest.skip("No lzma support")
    res = script.pip('install', data.packages / 'singlemodule-0.0.1.tar.lzma')
    assert "Successfully installed singlemodule-0.0.1" in res.stdout, res
1292 1293


1294
def test_double_install(script):
1295 1296 1297
    """
    Test double install passing with two same version requirements
    """
1298 1299
    result = script.pip('install', 'pip', 'pip',
                        expect_error=False)
1300 1301 1302 1303
    msg = "Double requirement given: pip (already in pip, name='pip')"
    assert msg not in result.stderr


1304
def test_double_install_fail(script):
1305 1306 1307 1308 1309 1310 1311
    """
    Test double install failing with two different version requirements
    """
    result = script.pip('install', 'pip==*', 'pip==7.1.2', expect_error=True)
    msg = ("Double requirement given: pip==7.1.2 (already in pip==*, "
           "name='pip')")
    assert msg in result.stderr
X
Xavier Fernandez 已提交
1312 1313


1314 1315 1316
def _get_expected_error_text():
    return (
        "Package 'pkga' requires a different Python: {} not in '<1.0'"
1317
    ).format('.'.join(map(str, sys.version_info[:3])))
1318 1319


1320
def test_install_incompatible_python_requires(script):
1321
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1322
    pkga_path = script.scratch_path / 'pkga'
1323
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1324 1325 1326 1327 1328 1329
        from setuptools import setup
        setup(name='pkga',
              python_requires='<1.0',
              version='0.1')
    """))
    result = script.pip('install', pkga_path, expect_error=True)
1330
    assert _get_expected_error_text() in result.stderr, str(result)
X
Xavier Fernandez 已提交
1331 1332


1333
def test_install_incompatible_python_requires_editable(script):
1334
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1335
    pkga_path = script.scratch_path / 'pkga'
1336
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1337 1338 1339 1340 1341 1342 1343
        from setuptools import setup
        setup(name='pkga',
              python_requires='<1.0',
              version='0.1')
    """))
    result = script.pip(
        'install', '--editable=%s' % pkga_path, expect_error=True)
1344
    assert _get_expected_error_text() in result.stderr, str(result)
X
Xavier Fernandez 已提交
1345 1346


1347
def test_install_incompatible_python_requires_wheel(script, with_wheel):
1348
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1349
    pkga_path = script.scratch_path / 'pkga'
1350
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1351 1352 1353 1354 1355 1356 1357 1358 1359
        from setuptools import setup
        setup(name='pkga',
              python_requires='<1.0',
              version='0.1')
    """))
    script.run(
        'python', 'setup.py', 'bdist_wheel', '--universal', cwd=pkga_path)
    result = script.pip('install', './pkga/dist/pkga-0.1-py2.py3-none-any.whl',
                        expect_error=True)
1360
    assert _get_expected_error_text() in result.stderr, str(result)
X
Xavier Fernandez 已提交
1361 1362


1363
def test_install_compatible_python_requires(script):
1364
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1365
    pkga_path = script.scratch_path / 'pkga'
1366
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1367 1368 1369 1370 1371
        from setuptools import setup
        setup(name='pkga',
              python_requires='>1.0',
              version='0.1')
    """))
C
Chris Hunt 已提交
1372
    res = script.pip('install', pkga_path)
X
Xavier Fernandez 已提交
1373
    assert "Successfully installed pkga-0.1" in res.stdout, res
1374 1375


1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
@pytest.mark.network
def test_install_pep508_with_url(script):
    res = script.pip(
        'install', '--no-index',
        'packaging@https://files.pythonhosted.org/packages/2f/2b/'
        'c681de3e1dbcd469537aefb15186b800209aa1f299d933d23b48d85c9d56/'
        'packaging-15.3-py2.py3-none-any.whl#sha256='
        'ce1a869fe039fbf7e217df36c4653d1dbe657778b2d41709593a0003584405f4'
    )
    assert "Successfully installed packaging-15.3" in str(res), str(res)


@pytest.mark.network
def test_install_pep508_with_url_in_install_requires(script):
    pkga_path = create_test_package_with_setup(
        script, name='pkga', version='1.0',
        install_requires=[
            'packaging@https://files.pythonhosted.org/packages/2f/2b/'
            'c681de3e1dbcd469537aefb15186b800209aa1f299d933d23b48d85c9d56/'
            'packaging-15.3-py2.py3-none-any.whl#sha256='
            'ce1a869fe039fbf7e217df36c4653d1dbe657778b2d41709593a0003584405f4'
        ],
    )
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423
    res = script.pip('install', pkga_path)
    assert "Successfully installed packaging-15.3" in str(res), str(res)


@pytest.mark.network
@pytest.mark.parametrize('index', (PyPI.simple_url, TestPyPI.simple_url))
def test_install_from_test_pypi_with_ext_url_dep_is_blocked(script, index):
    res = script.pip(
        'install',
        '--index-url',
        index,
        'pep-508-url-deps',
        expect_error=True,
    )
    error_message = (
        "Packages installed from PyPI cannot depend on packages "
        "which are not also hosted on PyPI."
    )
    error_cause = (
        "pep-508-url-deps depends on sampleproject@ "
        "https://github.com/pypa/sampleproject/archive/master.zip"
    )
    assert res.returncode == 1
    assert error_message in res.stderr, str(res)
    assert error_cause in res.stderr, str(res)
1424 1425 1426 1427


def test_installing_scripts_outside_path_prints_warning(script):
    result = script.pip_install_local(
C
Chris Hunt 已提交
1428
        "--prefix", script.scratch_path, "script_wheel1"
1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446
    )
    assert "Successfully installed script-wheel1" in result.stdout, str(result)
    assert "--no-warn-script-location" in result.stderr


def test_installing_scripts_outside_path_can_suppress_warning(script):
    result = script.pip_install_local(
        "--prefix", script.scratch_path, "--no-warn-script-location",
        "script_wheel1"
    )
    assert "Successfully installed script-wheel1" in result.stdout, str(result)
    assert "--no-warn-script-location" not in result.stderr


def test_installing_scripts_on_path_does_not_print_warning(script):
    result = script.pip_install_local("script_wheel1")
    assert "Successfully installed script-wheel1" in result.stdout, str(result)
    assert "--no-warn-script-location" not in result.stderr
1447 1448 1449 1450 1451 1452 1453


def test_installed_files_recorded_in_deterministic_order(script, data):
    """
    Ensure that we record the files installed by a package in a deterministic
    order, to make installs reproducible.
    """
1454
    to_install = data.packages.joinpath("FSPkg")
1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
    result = script.pip('install', to_install, expect_error=False)
    fspkg_folder = script.site_packages / 'fspkg'
    egg_info = 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
    installed_files_path = (
        script.site_packages / egg_info / 'installed-files.txt'
    )
    assert fspkg_folder in result.files_created, str(result.stdout)
    assert installed_files_path in result.files_created, str(result)

    installed_files_path = result.files_created[installed_files_path].full
    installed_files_lines = [
        p for p in Path(installed_files_path).read_text().split('\n') if p
    ]
    assert installed_files_lines == sorted(installed_files_lines)
P
Pradyun Gedam 已提交
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486


def test_install_conflict_results_in_warning(script, data):
    pkgA_path = create_test_package_with_setup(
        script,
        name='pkgA', version='1.0', install_requires=['pkgb == 1.0'],
    )
    pkgB_path = create_test_package_with_setup(
        script,
        name='pkgB', version='2.0',
    )

    # Install pkgA without its dependency
    result1 = script.pip('install', '--no-index', pkgA_path, '--no-deps')
    assert "Successfully installed pkgA-1.0" in result1.stdout, str(result1)

    # Then install an incorrect version of the dependency
    result2 = script.pip(
1487
        'install', '--no-index', pkgB_path, allow_stderr_error=True,
P
Pradyun Gedam 已提交
1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511
    )
    assert "pkga 1.0 has requirement pkgb==1.0" in result2.stderr, str(result2)
    assert "Successfully installed pkgB-2.0" in result2.stdout, str(result2)


def test_install_conflict_warning_can_be_suppressed(script, data):
    pkgA_path = create_test_package_with_setup(
        script,
        name='pkgA', version='1.0', install_requires=['pkgb == 1.0'],
    )
    pkgB_path = create_test_package_with_setup(
        script,
        name='pkgB', version='2.0',
    )

    # Install pkgA without its dependency
    result1 = script.pip('install', '--no-index', pkgA_path, '--no-deps')
    assert "Successfully installed pkgA-1.0" in result1.stdout, str(result1)

    # Then install an incorrect version of the dependency; suppressing warning
    result2 = script.pip(
        'install', '--no-index', pkgB_path, '--no-warn-conflicts'
    )
    assert "Successfully installed pkgB-2.0" in result2.stdout, str(result2)
1512 1513 1514 1515


def test_target_install_ignores_distutils_config_install_prefix(script):
    prefix = script.scratch_path / 'prefix'
B
Benoit Pierre 已提交
1516 1517 1518
    distutils_config = Path(os.path.expanduser('~'),
                            'pydistutils.cfg' if sys.platform == 'win32'
                            else '.pydistutils.cfg')
1519
    distutils_config.write_text(textwrap.dedent(
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530
        '''
        [install]
        prefix=%s
        ''' % str(prefix)))
    target = script.scratch_path / 'target'
    result = script.pip_install_local('simplewheel', '-t', target)
    assert (
        "Successfully installed simplewheel" in result.stdout and
        (target - script.base_path) in result.files_created and
        (prefix - script.base_path) not in result.files_created
    ), str(result)
1531 1532 1533


@pytest.mark.skipif("sys.platform != 'win32'")
A
Albert Tugushev 已提交
1534 1535 1536 1537 1538 1539 1540 1541 1542
@pytest.mark.parametrize('pip_name', [
    'pip',
    'pip{}'.format(sys.version_info[0]),
    'pip{}.{}'.format(*sys.version_info[:2]),
    'pip.exe',
    'pip{}.exe'.format(sys.version_info[0]),
    'pip{}.{}.exe'.format(*sys.version_info[:2])
])
def test_protect_pip_from_modification_on_windows(script, pip_name):
1543 1544 1545
    """
    Test ``pip install --upgrade pip`` is raised an error on Windows.
    """
A
Albert Tugushev 已提交
1546
    command = [pip_name, 'install', '--upgrade', 'pip']
1547 1548
    result = script.run(*command, expect_error=True)
    assert result.returncode != 0
A
Albert Tugushev 已提交
1549
    new_command = [sys.executable, '-m', 'pip'] + command[1:]
1550 1551 1552 1553 1554 1555 1556
    assert 'To modify pip, please run the following command:\n{}'.format(
        ' '.join(new_command)) in result.stderr, str(result)


@pytest.mark.skipif("sys.platform != 'win32'")
def test_protect_pip_from_modification_via_deps_on_windows(script, with_wheel):
    """
A
Albert Tugushev 已提交
1557
    Test ``pip install pkga`` is raised an error on Windows
1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
    if `pkga` implicitly tries to upgrade pip.
    """
    # Make a wheel for pkga which requires pip
    script.scratch_path.joinpath('pkga').mkdir()
    pkga_path = script.scratch_path / 'pkga'
    pkga_path.joinpath('setup.py').write_text(textwrap.dedent("""
        from setuptools import setup
        setup(name='pkga',
              version='0.1',
              install_requires = ["pip<{}"])
    """.format(pip.__version__)))
A
Albert Tugushev 已提交
1569
    script.run(
1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587
        'python', 'setup.py', 'bdist_wheel', '--universal', cwd=pkga_path
    )

    # Make sure pip install pkga is raised error
    pkga_wheel_path = './pkga/dist/pkga-0.1-py2.py3-none-any.whl'
    command = ['pip', 'install', pkga_wheel_path]
    result = script.run(*command, expect_error=True)
    assert result.returncode != 0
    new_command = [sys.executable, "-m"] + command
    assert "To modify pip, please run the following command:\n{}".format(
        " ".join(new_command)) in result.stderr, str(result)


@pytest.mark.skipif("sys.platform != 'win32'")
def test_protect_pip_from_modification_via_sub_deps_on_windows(
    script, with_wheel
):
    """
A
Albert Tugushev 已提交
1588 1589
    Test ``pip install pkg`` is raised an error on Windows
    if sub-dependencies of `pkg` implicitly tries to upgrade pip.
1590 1591 1592 1593 1594 1595 1596 1597 1598 1599
    """
    # Make a wheel for pkga which requires pip
    script.scratch_path.joinpath('pkga').mkdir()
    pkga_path = script.scratch_path / 'pkga'
    pkga_path.joinpath('setup.py').write_text(textwrap.dedent("""
        from setuptools import setup
        setup(name='pkga',
              version='0.1',
              install_requires = ["pip<{}"])
    """.format(pip.__version__)))
A
Albert Tugushev 已提交
1600
    script.run(
1601 1602 1603
        'python', 'setup.py', 'bdist_wheel', '--universal', cwd=pkga_path
    )

A
Albert Tugushev 已提交
1604
    # Make a wheel for pkgb which requires pkga
1605 1606 1607 1608 1609 1610 1611 1612
    script.scratch_path.joinpath('pkgb').mkdir()
    pkgb_path = script.scratch_path / 'pkgb'
    pkgb_path.joinpath('setup.py').write_text(textwrap.dedent("""
        from setuptools import setup
        setup(name='pkgb',
              version='0.1',
              install_requires = ["pkga"])
    """))
A
Albert Tugushev 已提交
1613
    script.run(
1614 1615 1616
        'python', 'setup.py', 'bdist_wheel', '--universal', cwd=pkgb_path
    )

A
Albert Tugushev 已提交
1617
    # Make sure pip install pkgb is raised an error
1618 1619 1620 1621 1622 1623 1624 1625 1626
    pkgb_wheel_path = './pkgb/dist/pkgb-0.1-py2.py3-none-any.whl'
    command = [
        'pip', 'install', pkgb_wheel_path, '--find-links', pkga_path / 'dist'
    ]
    result = script.run(*command, expect_error=True)
    assert result.returncode != 0
    new_command = [sys.executable, '-m'] + command
    assert 'To modify pip, please run the following command:\n{}'.format(
        ' '.join(new_command)) in result.stderr, str(result)