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

9
import pytest
H
Hugo 已提交
10
from pip._vendor.six import PY2
11

A
Albert Tugushev 已提交
12
from pip import __version__ as pip_current_version
13
from pip._internal import pep425tags
14
from pip._internal.cli.status_codes import ERROR, SUCCESS
15
from pip._internal.models.index import PyPI, TestPyPI
16
from pip._internal.utils.misc import rmtree
P
Pradyun S. Gedam 已提交
17
from tests.lib import (
18 19 20 21 22 23 24 25 26 27
    _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 已提交
28
)
29
from tests.lib.filesystem import make_socket_file
M
Marcus Smith 已提交
30 31
from tests.lib.local_repos import local_checkout
from tests.lib.path import Path
32

H
Hugo 已提交
33 34
skip_if_python2 = pytest.mark.skipif(PY2, reason="Non-Python 2 only")
skip_if_not_python2 = pytest.mark.skipif(not PY2, reason="Python 2 only")
H
Hugo 已提交
35

36

B
Benoit Pierre 已提交
37 38 39 40
@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 已提交
41
        script.pip("uninstall", "-y", "setuptools")
B
Benoit Pierre 已提交
42
    elif variant == 'bad_setuptools':
43
        setuptools_mod = script.site_packages_path.joinpath("setuptools.py")
44
        with open(setuptools_mod, 'a') as f:
B
Benoit Pierre 已提交
45 46
            f.write('\nraise ImportError("toto")')
    else:
B
Benoit Pierre 已提交
47 48 49
        raise ValueError(variant)
    script.pip(
        command, '--no-index', '-f', common_wheels, '-f', data.packages,
50
        data.src.joinpath("pep518-3.0"),
B
Benoit Pierre 已提交
51
    )
52 53


54 55
def test_pep518_build_env_uses_same_pip(
        script, data, pip_src, common_wheels, deprecated_python):
56 57 58 59 60 61 62 63
    """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,
64
        data.src.joinpath("pep518-3.0"),
65
        expect_stderr=deprecated_python,
66 67 68
    )


B
Benoit Pierre 已提交
69 70 71
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')
72
    project_dir = data.src.joinpath("pep518_conflicting_requires")
B
Benoit Pierre 已提交
73 74 75 76 77 78
    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 '
79
         'setuptools>=40.8.0.' % path_to_url(project_dir)) in result.stderr
B
Benoit Pierre 已提交
80 81 82
    ), str(result)


83
def test_pep518_refuses_invalid_requires(script, data, common_wheels):
84
    result = script.pip(
85
        'install', '-f', common_wheels,
86
        data.src.joinpath("pep518_invalid_requires"),
87 88 89 90 91 92
        expect_error=True
    )
    assert result.returncode == 1
    assert "does not comply with PEP 518" in result.stderr


93 94 95
def test_pep518_refuses_invalid_build_system(script, data, common_wheels):
    result = script.pip(
        'install', '-f', common_wheels,
96
        data.src.joinpath("pep518_invalid_build_system"),
97 98 99 100 101 102
        expect_error=True
    )
    assert result.returncode == 1
    assert "does not comply with PEP 518" in result.stderr


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

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

115 116 117 118
    assert result.returncode == 0
    assert result.files_created


119
def test_pep518_with_user_pip(script, pip_src, data, common_wheels):
120 121 122 123 124 125 126 127 128
    """
    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 已提交
129 130
    script.pip("install", "--ignore-installed",
               "-f", common_wheels, "--user", pip_src)
131
    system_pip_dir = script.site_packages_path / 'pip'
132
    assert not system_pip_dir.exists()
133 134 135
    system_pip_dir.mkdir()
    with open(system_pip_dir / '__init__.py', 'w') as fp:
        fp.write('raise ImportError\n')
B
Benoit Pierre 已提交
136 137
    script.pip(
        'wheel', '--no-index', '-f', common_wheels, '-f', data.packages,
138
        data.src.joinpath("pep518-3.0"),
B
Benoit Pierre 已提交
139
    )
140 141


142 143 144 145 146
def test_pep518_with_extra_and_markers(script, data, common_wheels):
    script.pip(
        'wheel', '--no-index',
        '-f', common_wheels,
        '-f', data.find_links,
147
        data.src.joinpath("pep518_with_extra_and_markers-1.0"),
148 149 150
    )


151 152 153 154 155
def test_pep518_with_namespace_package(script, data, common_wheels):
    script.pip(
        'wheel', '--no-index',
        '-f', common_wheels,
        '-f', data.find_links,
156
        data.src.joinpath("pep518_with_namespace_package-1.0"),
157 158 159 160
        use_module=True,
    )


161 162 163 164 165 166 167 168
@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(
169
        command, '--no-index', '-v',
170 171 172 173 174 175 176
        '-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),
177
    ) in result.stderr, str(result)
178 179


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

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


206 207 208 209 210 211 212 213 214 215 216 217 218
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
    """
219
    script.scratch_path.joinpath("blank.txt").write_text("\n")
220 221 222
    script.pip('install', '-r', 'blank.txt')


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

236 237 238 239
    # 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

240

241
def test_basic_editable_install(script):
242 243 244
    """
    Test editable installation.
    """
D
Donald Stufft 已提交
245
    result = script.pip('install', '-e', 'INITools==0.2', expect_error=True)
246
    assert (
247
        "INITools==0.2 is not a valid editable requirement"
248
        in result.stderr
249
    )
250 251
    assert not result.files_created
    assert not result.files_updated
252

253

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

267

268
def _test_install_editable_from_git(script, tmpdir):
269 270 271
    """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 已提交
272
    result = script.pip(*args)
273
    result.assert_installed('testpackage', with_files=['.git'])
274

275

276
def test_basic_install_editable_from_git(script, tmpdir):
277
    _test_install_editable_from_git(script, tmpdir)
278 279


280
def test_install_editable_from_git_autobuild_wheel(
281
        script, tmpdir, with_wheel):
282
    _test_install_editable_from_git(script, tmpdir)
283 284


X
Xavier Fernandez 已提交
285
@pytest.mark.network
286 287 288 289 290 291 292
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
    """
293
    to_install = data.packages.joinpath("pip-test-package-0.1.tar.gz")
294 295 296 297 298 299 300 301
    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(
302
            'git+https://github.com/pypa/pip-test-package.git', tmpdir,
303 304 305 306
        ),
    )
    result.assert_installed('pip-test-package', with_files=['.git'])
    assert 'Found existing installation: pip-test-package 0.1' in result.stdout
307
    assert 'Uninstalling pip-test-package-' in result.stdout
308 309 310
    assert 'Successfully uninstalled pip-test-package' in result.stdout


311 312 313 314 315
def test_install_editable_uninstalls_existing_from_path(script, data):
    """
    Test that installing an editable uninstalls a previously installed
    non-editable version from path
    """
316
    to_install = data.src.joinpath('simplewheel-1.0')
317 318
    result = script.pip_install_local(to_install)
    assert 'Successfully installed simplewheel' in result.stdout
319 320
    simple_folder = script.site_packages / 'simplewheel'
    result.assert_installed('simplewheel', editable=False)
321 322 323 324 325 326 327 328 329 330 331 332 333 334
    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)


335
@need_mercurial
336
def test_basic_install_editable_from_hg(script, tmpdir):
337 338 339
    """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 已提交
340
    result = script.pip(*args)
341
    result.assert_installed('testpackage', with_files=['.hg'])
342

343

344
@need_mercurial
345
def test_vcs_url_final_slash_normalization(script, tmpdir):
346 347 348
    """
    Test that presence or absence of final slash in VCS URL is normalized.
    """
349 350
    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 已提交
351
    result = script.pip(*args)
352
    result.assert_installed('testpackage', with_files=['.hg'])
353

354

355
@need_bzr
356
def test_install_editable_from_bazaar(script, tmpdir):
357 358 359
    """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 已提交
360
    result = script.pip(*args)
361
    result.assert_installed('testpackage', with_files=['.bzr'])
F
Francesco 已提交
362

363

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

380

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

394

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

    # Compute relative install path to FSPkg from scratch path.
408
    full_rel_path = data.packages.joinpath('FSPkg') - script.scratch_path
409 410 411
    full_rel_url = (
        'file:' + full_rel_path.replace(os.path.sep, '/') + '#egg=FSPkg'
    )
412
    embedded_rel_path = script.scratch_path.joinpath(full_rel_path)
413 414 415

    # For each relative path, install as either editable or not using either
    # URLs with egg links or not.
416
    for req_path in (full_rel_path, full_rel_url, embedded_rel_path):
417 418 419 420 421
        # 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)
422 423
        script.pip('uninstall', '-y', 'fspkg')

424 425 426 427 428
        # 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')
429 430


431 432 433 434 435 436 437 438
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
439
    to_install = data.packages.joinpath("FSPkg")
440
    result = script.pip('install', '-qqq', to_install, expect_error=False)
441 442 443 444
    assert result.stdout == ""
    assert result.stderr == ""


445 446 447 448 449 450 451 452 453 454 455
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 已提交
456 457 458 459 460 461 462
    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)
463 464


465
def test_hashed_install_failure(script, tmpdir):
466 467 468 469 470 471 472
    """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 已提交
473 474
    with requirements_file('simple2==1.0 --hash=sha256:9336af72ca661e6336eb87b'
                           'c7de3e8844d853e3848c2b9bbd2e8bf01db88c2c\n',
475 476 477 478 479 480 481
                           tmpdir) as reqs_file:
        result = script.pip_install_local('-r',
                                          reqs_file.abspath,
                                          expect_error=True)
    assert len(result.files_created) == 0


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


497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
@pytest.mark.skipif("sys.platform == 'win32' or sys.version_info < (3,)")
def test_install_from_local_directory_with_socket_file(script, data, tmpdir):
    """
    Test installing from a local directory containing a socket file.
    """
    egg_info_file = (
        script.site_packages / "FSPkg-0.1.dev0-py%s.egg-info" % pyversion
    )
    package_folder = script.site_packages / "fspkg"
    to_copy = data.packages.joinpath("FSPkg")
    to_install = tmpdir.joinpath("src")

    shutil.copytree(to_copy, to_install)
    # Socket file, should be ignored.
    socket_file_path = os.path.join(to_install, "example")
    make_socket_file(socket_file_path)

    result = script.pip("install", "--verbose", to_install, expect_error=False)
    assert package_folder in result.files_created, str(result.stdout)
    assert egg_info_file in result.files_created, str(result)
    assert str(socket_file_path) in result.stderr


520
def test_install_from_local_directory_with_no_setup_py(script, data):
521 522 523
    """
    Test installing from a local directory with no 'setup.py'.
    """
524
    result = script.pip('install', data.root, expect_error=True)
525
    assert not result.files_created
526 527
    assert "is not installable." in result.stderr
    assert "Neither 'setup.py' nor 'pyproject.toml' found." in result.stderr
528

529

530 531
def test_editable_install__local_dir_no_setup_py(
        script, data, deprecated_python):
532
    """
533
    Test installing in editable mode from a local directory with no setup.py.
534
    """
535
    result = script.pip('install', '-e', data.root, expect_error=True)
536
    assert not result.files_created
537 538 539 540 541

    msg = result.stderr
    if deprecated_python:
        assert 'File "setup.py" not found. ' in msg
    else:
542
        assert msg.startswith('ERROR: File "setup.py" not found. ')
543 544 545 546 547 548 549 550 551
    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.
    """
552 553 554
    local_dir = script.scratch_path.joinpath('temp').mkdir()
    pyproject_path = local_dir.joinpath('pyproject.toml')
    pyproject_path.write_text('')
555 556 557 558 559 560 561 562

    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:
563
        assert msg.startswith('ERROR: File "setup.py" not found. ')
564
    assert 'A "pyproject.toml" file was found' in msg
565 566


H
Hugo 已提交
567
@skip_if_not_python2
568
@pytest.mark.xfail
569
def test_install_argparse_shadowed(script):
570
    # When argparse is in the stdlib, we support installing it
J
Jakub Wilk 已提交
571
    # even though that's pretty useless because older packages did need to
572 573 574 575 576 577 578 579 580
    # 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 已提交
581
@pytest.mark.network
H
Hugo 已提交
582
@skip_if_python2
583
def test_upgrade_argparse_shadowed(script):
584 585 586 587 588 589 590
    # 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


591
def test_install_curdir(script, data):
592 593 594
    """
    Test installing current directory ('.').
    """
595
    run_from = data.packages.joinpath("FSPkg")
596 597 598 599
    # 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 已提交
600
    result = script.pip('install', curdir, cwd=run_from, expect_error=False)
D
Donald Stufft 已提交
601
    fspkg_folder = script.site_packages / 'fspkg'
602
    egg_info_folder = (
D
Donald Stufft 已提交
603
        script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
604
    )
605 606
    assert fspkg_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)
607

608

609
def test_install_pardir(script, data):
610 611 612
    """
    Test installing parent directory ('..').
    """
613
    run_from = data.packages.joinpath("FSPkg", "fspkg")
D
Donald Stufft 已提交
614
    result = script.pip('install', pardir, cwd=run_from, expect_error=False)
D
Donald Stufft 已提交
615
    fspkg_folder = script.site_packages / 'fspkg'
616
    egg_info_folder = (
D
Donald Stufft 已提交
617
        script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
618
    )
619 620
    assert fspkg_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)
621 622


623
@pytest.mark.network
624
def test_install_global_option(script):
625
    """
626 627
    Test using global distutils options.
    (In particular those that disable the actual install action)
628
    """
629 630
    result = script.pip(
        'install', '--global-option=--version', "INITools==0.1",
631
        expect_stderr=True)
632
    assert 'INITools==0.1\n' in result.stdout
633 634


635
def test_install_with_hacked_egg_info(script, data):
636 637 638
    """
    test installing a package which defines its own egg_info class
    """
639
    run_from = data.packages.joinpath("HackedEggInfo")
D
Donald Stufft 已提交
640
    result = script.pip('install', '.', cwd=run_from)
641
    assert 'Successfully installed hackedegginfo-0.0.0\n' in result.stdout
642 643


644
@pytest.mark.network
645
def test_install_using_install_option_and_editable(script, tmpdir):
646 647 648
    """
    Test installing a tool using -e and --install-option
    """
H
Hugo Lopes Tavares 已提交
649
    folder = 'script_folder'
650
    script.scratch_path.joinpath(folder).mkdir()
651
    url = 'git+git://github.com/pypa/pip-test-package'
652
    result = script.pip(
653
        'install', '-e', '%s#egg=pip-test-package' %
654
        local_checkout(url, tmpdir),
655 656
        '--install-option=--script-dir=%s' % folder,
        expect_stderr=True)
657 658 659
    script_file = (
        script.venv / 'src' / 'pip-test-package' /
        folder / 'pip-test-package' + script.exe
660
    )
661
    assert script_file in result.files_created
662 663


664
@pytest.mark.network
665
@need_mercurial
666
def test_install_global_option_using_editable(script, tmpdir):
667 668 669
    """
    Test using global distutils options, but in an editable installation
    """
C
Carl Meyer 已提交
670
    url = 'hg+http://bitbucket.org/runeh/anyjson'
671 672
    result = script.pip(
        'install', '--global-option=--version', '-e',
673
        '%s@0.2.5#egg=anyjson' % local_checkout(url, tmpdir),
674
        expect_stderr=True)
675
    assert 'Successfully installed anyjson' in result.stdout
676 677


678
@pytest.mark.network
679
def test_install_package_with_same_name_in_curdir(script):
680 681 682
    """
    Test installing a package with the same name of a local folder
    """
683
    script.scratch_path.joinpath("mock==0.6").mkdir()
D
Donald Stufft 已提交
684 685
    result = script.pip('install', 'mock==0.6')
    egg_folder = script.site_packages / 'mock-0.6.0-py%s.egg-info' % pyversion
686 687 688 689 690 691 692 693 694
    assert egg_folder in result.files_created, str(result)


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


695
def test_install_folder_using_dot_slash(script):
696 697 698
    """
    Test installing a folder using pip install ./foldername
    """
699
    script.scratch_path.joinpath("mock").mkdir()
D
Donald Stufft 已提交
700
    pkg_path = script.scratch_path / 'mock'
701
    pkg_path.joinpath("setup.py").write_text(mock100_setup_py)
D
Donald Stufft 已提交
702 703
    result = script.pip('install', './mock')
    egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion
704 705 706
    assert egg_folder in result.files_created, str(result)


707
def test_install_folder_using_slash_in_the_end(script):
708 709
    r"""
    Test installing a folder using pip install foldername/ or foldername\
710
    """
711
    script.scratch_path.joinpath("mock").mkdir()
D
Donald Stufft 已提交
712
    pkg_path = script.scratch_path / 'mock'
713
    pkg_path.joinpath("setup.py").write_text(mock100_setup_py)
D
Donald Stufft 已提交
714 715
    result = script.pip('install', 'mock' + os.path.sep)
    egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion
716 717 718
    assert egg_folder in result.files_created, str(result)


719
def test_install_folder_using_relative_path(script):
720 721 722
    """
    Test installing a folder using pip install folder1/folder2
    """
723 724
    script.scratch_path.joinpath("initools").mkdir()
    script.scratch_path.joinpath("initools", "mock").mkdir()
D
Donald Stufft 已提交
725
    pkg_path = script.scratch_path / 'initools' / 'mock'
726
    pkg_path.joinpath("setup.py").write_text(mock100_setup_py)
D
Donald Stufft 已提交
727
    result = script.pip('install', Path('initools') / 'mock')
D
Donald Stufft 已提交
728
    egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion
729
    assert egg_folder in result.files_created, str(result)
730

731

732
@pytest.mark.network
733
def test_install_package_which_contains_dev_in_name(script):
734
    """
J
Jon Dufresne 已提交
735
    Test installing package from PyPI which contains 'dev' in name
736
    """
D
Donald Stufft 已提交
737
    result = script.pip('install', 'django-devserver==0.0.4')
D
Donald Stufft 已提交
738
    devserver_folder = script.site_packages / 'devserver'
739
    egg_info_folder = (
D
Donald Stufft 已提交
740 741
        script.site_packages / 'django_devserver-0.0.4-py%s.egg-info' %
        pyversion
742
    )
743 744
    assert devserver_folder in result.files_created, str(result.stdout)
    assert egg_info_folder in result.files_created, str(result)
745

746

747
def test_install_package_with_target(script):
748 749 750
    """
    Test installing a package using pip install --target
    """
D
Donald Stufft 已提交
751
    target_dir = script.scratch_path / 'target'
752 753
    result = script.pip_install_local('-t', target_dir, "simple==1.0")
    assert Path('scratch') / 'target' / 'simple' in result.files_created, (
754 755
        str(result)
    )
756

757
    # Test repeated call without --upgrade, no files should have changed
758 759 760
    result = script.pip_install_local(
        '-t', target_dir, "simple==1.0", expect_stderr=True,
    )
761
    assert not Path('scratch') / 'target' / 'simple' in result.files_updated
762 763

    # Test upgrade call, check that new version is installed
764 765 766
    result = script.pip_install_local('--upgrade', '-t',
                                      target_dir, "simple==2.0")
    assert Path('scratch') / 'target' / 'simple' in result.files_updated, (
767 768
        str(result)
    )
J
Jon Wayne Parrott 已提交
769
    egg_folder = (
770
        Path('scratch') / 'target' / 'simple-2.0-py%s.egg-info' % pyversion)
J
Jon Wayne Parrott 已提交
771
    assert egg_folder in result.files_created, (
772 773 774
        str(result)
    )

775
    # Test install and upgrade of single-module package
776 777 778
    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)
779

780 781 782
    result = script.pip_install_local('-t', target_dir, 'singlemodule==0.0.1',
                                      '--upgrade')
    assert singlemodule_py in result.files_updated, str(result)
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 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
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


845
def test_install_with_target_and_scripts_no_warning(script, with_wheel):
846 847 848 849 850 851 852
    """
    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()
853
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
854 855 856 857 858 859 860 861 862
        from setuptools import setup
        setup(name='pkga',
              version='0.1',
              py_modules=["pkga"],
              entry_points={
                  'console_scripts': ['pkga=pkga:main']
              }
        )
    """))
863
    pkga_path.joinpath("pkga.py").write_text(textwrap.dedent("""
864 865 866 867 868 869 870 871 872
        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)


873
def test_install_package_with_root(script, data):
874 875 876
    """
    Test installing a package using pip install --root
    """
D
Donald Stufft 已提交
877
    root_dir = script.scratch_path / 'root'
878 879 880 881 882 883 884 885 886
    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 已提交
887
    from distutils.util import change_root
888 889 890 891
    root_path = change_root(
        os.path.join(script.scratch, 'root'),
        normal_install_path
    )
M
Marcus Smith 已提交
892
    assert root_path in result.files_created, str(result)
893

894 895 896 897
    # Should show find-links location in output
    assert "Looking in indexes: " not in result.stdout
    assert "Looking in links: " in result.stdout

898

899 900 901 902 903 904 905
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,
906
        '--no-binary', 'simple', '--no-index', 'simple==1.0',
907 908
    )

909
    rel_prefix_path = script.scratch / 'prefix'
910
    install_path = (
911
        distutils.sysconfig.get_python_lib(prefix=rel_prefix_path) /
A
Anthony Sottile 已提交
912
        'simple-1.0-py{}.egg-info'.format(pyversion)
913 914 915 916
    )
    assert install_path in result.files_created, str(result)


917 918 919 920
def test_install_editable_with_prefix(script):
    # make a dummy project
    pkga_path = script.scratch_path / 'pkga'
    pkga_path.mkdir()
921
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
922 923 924 925 926
        from setuptools import setup
        setup(name='pkga',
              version='0.1')
    """))

927 928
    if hasattr(sys, "pypy_version_info"):
        site_packages = os.path.join(
A
Anthony Sottile 已提交
929
            'prefix', 'lib', 'python{}'.format(pyversion), 'site-packages')
930 931
    else:
        site_packages = distutils.sysconfig.get_python_lib(prefix='prefix')
932 933 934

    # make sure target path is in PYTHONPATH
    pythonpath = script.scratch_path / site_packages
935
    pythonpath.mkdir(parents=True)
936 937 938 939 940 941 942 943 944 945 946 947
    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)


948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
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
    )


963
def test_install_package_that_emits_unicode(script, data):
964
    """
965
    Install a package with a setup.py that emits UTF-8 output and then fails.
966

967
    Refs https://github.com/pypa/pip/issues/326
968
    """
969
    to_install = data.packages.joinpath("BrokenEmitsUTF8")
970 971 972 973
    result = script.pip(
        'install', to_install, expect_error=True, expect_temp=True, quiet=True,
    )
    assert (
974
        'FakeError: this package designed to fail on install' in result.stderr
975
    ), 'stderr: {}'.format(result.stderr)
976
    assert 'UnicodeDecodeError' not in result.stderr
977
    assert 'UnicodeDecodeError' not in result.stdout
978

979

980 981
def test_install_package_with_utf8_setup(script, data):
    """Install a package with a setup.py that declares a utf-8 encoding."""
982
    to_install = data.packages.joinpath("SetupPyUTF8")
983 984
    script.pip('install', to_install)

985

986 987
def test_install_package_with_latin1_setup(script, data):
    """Install a package with a setup.py that declares a latin-1 encoding."""
988
    to_install = data.packages.joinpath("SetupPyLatin1")
989
    script.pip('install', to_install)
990

991

992
def test_url_req_case_mismatch_no_index(script, data):
993
    """
994 995 996
    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.
997

998
    tests/data/packages contains Upper-1.0.tar.gz and Upper-2.0.tar.gz
999 1000
    'requiresupper' has install_requires = ['upper']
    """
1001
    Upper = '/'.join((data.find_links, 'Upper-1.0.tar.gz'))
1002 1003 1004
    result = script.pip(
        'install', '--no-index', '-f', data.find_links, Upper, 'requiresupper'
    )
1005

1006
    # only Upper-1.0.tar.gz should get installed.
D
Donald Stufft 已提交
1007
    egg_folder = script.site_packages / 'Upper-1.0-py%s.egg-info' % pyversion
1008
    assert egg_folder in result.files_created, str(result)
D
Donald Stufft 已提交
1009
    egg_folder = script.site_packages / 'Upper-2.0-py%s.egg-info' % pyversion
1010
    assert egg_folder not in result.files_created, str(result)
1011 1012


1013 1014 1015 1016 1017 1018
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.

1019
    tests/data/packages3 contains Dinner-1.0.tar.gz and Dinner-2.0.tar.gz
1020 1021 1022 1023 1024 1025 1026 1027
    '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.
    """
1028
    Dinner = '/'.join((data.find_links3, 'dinner', 'Dinner-1.0.tar.gz'))
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
    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",
1065
        expect_stderr=True,
1066 1067 1068 1069 1070 1071 1072 1073
    )

    # 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)

1074 1075 1076 1077
    # Should show index-url location in output
    assert "Looking in indexes: " in result.stdout
    assert "Looking in links: " not in result.stdout

1078

1079
@pytest.mark.network
1080 1081 1082 1083 1084
def test_compiles_pyc(script):
    """
    Test installing with --compile on
    """
    del script.environ["PYTHONDONTWRITEBYTECODE"]
1085
    script.pip("install", "--compile", "--no-binary=:all:", "INITools==0.2")
1086 1087 1088 1089

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

    exists += glob.glob(
D
Donald Stufft 已提交
1094
        script.site_packages_path / "initools/__pycache__/__init__*.pyc"
1095 1096 1097 1098 1099
    )

    assert any(exists)


1100
@pytest.mark.network
1101
def test_no_compiles_pyc(script):
1102 1103 1104 1105
    """
    Test installing from wheel with --compile on
    """
    del script.environ["PYTHONDONTWRITEBYTECODE"]
1106
    script.pip("install", "--no-compile", "--no-binary=:all:", "INITools==0.2")
1107 1108 1109 1110

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

    exists += glob.glob(
D
Donald Stufft 已提交
1115
        script.site_packages_path / "initools/__pycache__/__init__*.pyc"
1116 1117 1118
    )

    assert not any(exists)
1119 1120 1121


def test_install_upgrade_editable_depending_on_other_editable(script):
1122
    script.scratch_path.joinpath("pkga").mkdir()
1123
    pkga_path = script.scratch_path / 'pkga'
1124
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
1125 1126 1127 1128 1129
        from setuptools import setup
        setup(name='pkga',
              version='0.1')
    """))
    script.pip('install', '--editable', pkga_path)
X
Xavier Fernandez 已提交
1130 1131
    result = script.pip('list', '--format=freeze')
    assert "pkga==0.1" in result.stdout
1132

1133
    script.scratch_path.joinpath("pkgb").mkdir()
1134
    pkgb_path = script.scratch_path / 'pkgb'
1135
    pkgb_path.joinpath("setup.py").write_text(textwrap.dedent("""
1136 1137 1138 1139 1140
        from setuptools import setup
        setup(name='pkgb',
              version='0.1',
              install_requires=['pkga'])
    """))
X
Xavier Fernandez 已提交
1141 1142 1143
    script.pip('install', '--upgrade', '--editable', pkgb_path, '--no-index')
    result = script.pip('list', '--format=freeze')
    assert "pkgb==0.1" in result.stdout
1144

1145 1146

def test_install_subprocess_output_handling(script, data):
1147
    args = ['install', data.src.joinpath('chattymodule')]
1148 1149 1150 1151 1152 1153 1154 1155 1156

    # 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.
1157 1158
    result = script.pip(*(args + ["--verbose"]), expect_stderr=True)
    assert 1 == result.stderr.count("HELLO FROM CHATTYMODULE egg_info")
1159 1160 1161 1162 1163 1164
    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)
1165
    assert 1 == result.stderr.count("I DIE, I DIE")
1166 1167 1168

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

1171 1172 1173

def test_install_log(script, data, tmpdir):
    # test that verbose logs go to "--log" file
1174
    f = tmpdir.joinpath("log.txt")
1175
    args = ['--log=%s' % f,
1176
            'install', data.src.joinpath('chattymodule')]
1177 1178 1179 1180 1181 1182
    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")

1183 1184

def test_install_topological_sort(script, data):
X
Xavier Fernandez 已提交
1185
    args = ['install', 'TopoRequires4', '--no-index', '-f', data.packages]
1186 1187 1188 1189
    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
1190 1191


1192 1193
def test_install_wheel_broken(script, with_wheel):
    res = script.pip_install_local('wheelbroken', expect_stderr=True)
1194
    assert "Successfully installed wheelbroken-0.1" in str(res), str(res)
1195 1196


1197 1198
def test_cleanup_after_failed_wheel(script, with_wheel):
    res = script.pip_install_local('wheelbrokenafter', expect_stderr=True)
1199 1200
    # One of the effects of not cleaning up is broken scripts:
    script_py = script.bin_path / "script.py"
1201
    assert script_py.exists(), script_py
1202 1203 1204 1205 1206 1207
    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)


1208
def test_install_builds_wheels(script, data, with_wheel):
1209 1210 1211 1212 1213 1214
    # 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')
1215 1216 1217
    # NB This incidentally tests a local tree + tarball inputs
    # see test_install_editable_from_git_autobuild_wheel for editable
    # vcs coverage.
1218
    to_install = data.packages.joinpath('requires_wheelbroken_upper')
1219
    res = script.pip(
1220
        'install', '--no-index', '-f', data.find_links,
1221 1222 1223 1224 1225 1226
        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 = []
1227
    for top, dirs, files in os.walk(wheels_cache):
1228 1229
        wheels.extend(files)
    # and built wheels for upper and wheelbroken
P
Paul Moore 已提交
1230 1231
    assert "Building wheel for upper" in str(res), str(res)
    assert "Building wheel for wheelb" in str(res), str(res)
T
Thomas Kluyver 已提交
1232
    # Wheels are built for local directories, but not cached.
P
Paul Moore 已提交
1233
    assert "Building wheel for requir" in str(res), str(res)
1234 1235 1236 1237 1238
    # 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 已提交
1239 1240
    # Wheels are built for local directories, but not cached.
    assert "Running setup.py install for requir" not in str(res), str(res)
1241 1242
    # wheelbroken has to run install
    assert "Running setup.py install for wheelb" in str(res), str(res)
1243 1244
    # We want to make sure we used the correct implementation tag
    assert wheels == [
A
Anthony Sottile 已提交
1245
        "Upper-2.0-{}-none-any.whl".format(pep425tags.implementation_tag),
1246
    ]
1247 1248


1249
def test_install_no_binary_disables_building_wheels(script, data, with_wheel):
1250
    to_install = data.packages.joinpath('requires_wheelbroken_upper')
1251 1252 1253 1254 1255 1256 1257 1258
    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 已提交
1259
    assert "Building wheel for wheelb" in str(res), str(res)
1260
    # Wheels are built for local directories, but not cached across runs
P
Paul Moore 已提交
1261
    assert "Building wheel for requir" in str(res), str(res)
1262
    # Don't build wheel for upper which was blacklisted
P
Paul Moore 已提交
1263
    assert "Building wheel for upper" not in str(res), str(res)
1264
    # Wheels are built for local directories, but not cached across runs
1265
    assert "Running setup.py install for requir" not in str(res), str(res)
1266
    # And these two fell back to sdist based installed.
1267 1268
    assert "Running setup.py install for wheelb" in str(res), str(res)
    assert "Running setup.py install for upper" in str(res), str(res)
1269 1270


1271
def test_install_no_binary_disables_cached_wheels(script, data, with_wheel):
1272 1273
    # Seed the cache
    script.pip(
1274
        'install', '--no-index', '-f', data.find_links,
1275 1276 1277 1278 1279 1280 1281
        '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 已提交
1282
    assert "Building wheel for upper" not in str(res), str(res)
1283 1284
    # Must have used source, not a cached wheel to install upper.
    assert "Running setup.py install for upper" in str(res), str(res)
1285 1286 1287


def test_install_editable_with_wrong_egg_name(script):
1288
    script.scratch_path.joinpath("pkga").mkdir()
1289
    pkga_path = script.scratch_path / 'pkga'
1290
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
1291 1292 1293 1294 1295
        from setuptools import setup
        setup(name='pkga',
              version='0.1')
    """))
    result = script.pip(
C
Chris Hunt 已提交
1296 1297
        'install', '--editable', 'file://%s#egg=pkgb' % pkga_path
    )
P
Paul Moore 已提交
1298
    assert ("Generating metadata for package pkgb produced metadata "
1299 1300 1301
            "for project name pkga. Fix your #egg=pkgb "
            "fragments.") in result.stderr
    assert "Successfully installed pkga" in str(result), str(result)
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319


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
1320 1321


1322
def test_double_install(script):
1323 1324 1325
    """
    Test double install passing with two same version requirements
    """
1326 1327
    result = script.pip('install', 'pip', 'pip',
                        expect_error=False)
1328 1329 1330 1331
    msg = "Double requirement given: pip (already in pip, name='pip')"
    assert msg not in result.stderr


1332
def test_double_install_fail(script):
1333 1334 1335 1336 1337 1338 1339
    """
    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 已提交
1340 1341


1342 1343 1344
def _get_expected_error_text():
    return (
        "Package 'pkga' requires a different Python: {} not in '<1.0'"
1345
    ).format('.'.join(map(str, sys.version_info[:3])))
1346 1347


1348
def test_install_incompatible_python_requires(script):
1349
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1350
    pkga_path = script.scratch_path / 'pkga'
1351
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1352 1353 1354 1355 1356 1357
        from setuptools import setup
        setup(name='pkga',
              python_requires='<1.0',
              version='0.1')
    """))
    result = script.pip('install', pkga_path, expect_error=True)
1358
    assert _get_expected_error_text() in result.stderr, str(result)
X
Xavier Fernandez 已提交
1359 1360


1361
def test_install_incompatible_python_requires_editable(script):
1362
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1363
    pkga_path = script.scratch_path / 'pkga'
1364
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1365 1366 1367 1368 1369 1370 1371
        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)
1372
    assert _get_expected_error_text() in result.stderr, str(result)
X
Xavier Fernandez 已提交
1373 1374


1375
def test_install_incompatible_python_requires_wheel(script, with_wheel):
1376
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1377
    pkga_path = script.scratch_path / 'pkga'
1378
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1379 1380 1381 1382 1383 1384 1385 1386 1387
        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)
1388
    assert _get_expected_error_text() in result.stderr, str(result)
X
Xavier Fernandez 已提交
1389 1390


1391
def test_install_compatible_python_requires(script):
1392
    script.scratch_path.joinpath("pkga").mkdir()
X
Xavier Fernandez 已提交
1393
    pkga_path = script.scratch_path / 'pkga'
1394
    pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
X
Xavier Fernandez 已提交
1395 1396 1397 1398 1399
        from setuptools import setup
        setup(name='pkga',
              python_requires='>1.0',
              version='0.1')
    """))
C
Chris Hunt 已提交
1400
    res = script.pip('install', pkga_path)
X
Xavier Fernandez 已提交
1401
    assert "Successfully installed pkga-0.1" in res.stdout, res
1402 1403


1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
@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'
        ],
    )
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451
    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)
1452 1453 1454 1455


def test_installing_scripts_outside_path_prints_warning(script):
    result = script.pip_install_local(
C
Chris Hunt 已提交
1456
        "--prefix", script.scratch_path, "script_wheel1"
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474
    )
    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
1475 1476 1477 1478 1479 1480 1481


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.
    """
1482
    to_install = data.packages.joinpath("FSPkg")
1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
    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 已提交
1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514


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(
1515
        'install', '--no-index', pkgB_path, allow_stderr_error=True,
P
Pradyun Gedam 已提交
1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
    )
    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)
1540 1541 1542 1543


def test_target_install_ignores_distutils_config_install_prefix(script):
    prefix = script.scratch_path / 'prefix'
B
Benoit Pierre 已提交
1544 1545 1546
    distutils_config = Path(os.path.expanduser('~'),
                            'pydistutils.cfg' if sys.platform == 'win32'
                            else '.pydistutils.cfg')
1547
    distutils_config.write_text(textwrap.dedent(
1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558
        '''
        [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)
1559 1560


A
Albert Tugushev 已提交
1561
@pytest.mark.network
1562
@pytest.mark.skipif("sys.platform != 'win32'")
A
Albert Tugushev 已提交
1563 1564 1565 1566 1567 1568 1569 1570 1571
@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):
1572
    """
A
Albert Tugushev 已提交
1573 1574
    Test that pip modification command using ``pip install ...``
    raises an error on Windows.
1575
    """
A
Albert Tugushev 已提交
1576
    command = [pip_name, 'install', 'pip != {}'.format(pip_current_version)]
1577
    result = script.run(*command, expect_error=True)
A
Albert Tugushev 已提交
1578
    new_command = [sys.executable, '-m', 'pip'] + command[1:]
A
Albert Tugushev 已提交
1579 1580 1581 1582 1583
    expected_message = (
        'To modify pip, please run the following command:\n{}'
        .format(' '.join(new_command))
    )
    assert expected_message in result.stderr, str(result)
1584 1585


A
Albert Tugushev 已提交
1586
@pytest.mark.network
1587
@pytest.mark.skipif("sys.platform != 'win32'")
A
Albert Tugushev 已提交
1588
def test_protect_pip_from_modification_via_deps_on_windows(script):
1589
    """
A
Albert Tugushev 已提交
1590
    Test ``pip install pkga`` raises an error on Windows
1591 1592
    if `pkga` implicitly tries to upgrade pip.
    """
A
Albert Tugushev 已提交
1593 1594 1595 1596
    pkga_wheel_path = create_basic_wheel_for_package(
        script,
        'pkga', '0.1',
        depends=['pip != {}'.format(pip_current_version)],
1597 1598
    )

A
Albert Tugushev 已提交
1599 1600 1601 1602
    # Make sure pip install pkga raises an error
    args = ['install', pkga_wheel_path]
    result = script.pip(*args, expect_error=True, use_module=False)
    new_command = [sys.executable, '-m', 'pip'] + args
A
Albert Tugushev 已提交
1603 1604 1605 1606 1607
    expected_message = (
        'To modify pip, please run the following command:\n{}'
        .format(' '.join(new_command))
    )
    assert expected_message in result.stderr, str(result)
1608 1609


A
Albert Tugushev 已提交
1610
@pytest.mark.network
1611
@pytest.mark.skipif("sys.platform != 'win32'")
A
Albert Tugushev 已提交
1612
def test_protect_pip_from_modification_via_sub_deps_on_windows(script):
1613
    """
A
Albert Tugushev 已提交
1614
    Test ``pip install pkga`` raises an error on Windows
A
Albert Tugushev 已提交
1615
    if sub-dependencies of `pkga` implicitly tries to upgrade pip.
1616
    """
A
Albert Tugushev 已提交
1617 1618 1619 1620 1621
    # Make a wheel for pkga which requires pkgb
    pkga_wheel_path = create_basic_wheel_for_package(
        script,
        'pkga', '0.1',
        depends=['pkgb'],
1622 1623
    )

A
Albert Tugushev 已提交
1624 1625 1626 1627 1628
    # Make a wheel for pkgb which requires pip
    pkgb_wheel_path = create_basic_wheel_for_package(
        script,
        'pkgb', '0.1',
        depends=['pip != {}'.format(pip_current_version)],
1629 1630
    )

A
Albert Tugushev 已提交
1631 1632 1633
    # Make sure pip install pkga raises an error
    args = [
        'install', pkga_wheel_path, '--find-links', pkgb_wheel_path.parent
1634
    ]
A
Albert Tugushev 已提交
1635 1636
    result = script.pip(*args, expect_error=True, use_module=False)
    new_command = [sys.executable, '-m', 'pip'] + args
A
Albert Tugushev 已提交
1637 1638 1639 1640 1641
    expected_message = (
        'To modify pip, please run the following command:\n{}'
        .format(' '.join(new_command))
    )
    assert expected_message in result.stderr, str(result)
1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659


@pytest.mark.parametrize(
    'install_args, expected_message', [
        ([], 'Requirement already satisfied: pip in'),
        (['--upgrade'], 'Requirement already up-to-date: pip in'),
    ]
)
@pytest.mark.parametrize("use_module", [True, False])
def test_install_pip_does_not_modify_pip_when_satisfied(
        script, install_args, expected_message, use_module):
    """
    Test it doesn't upgrade the pip if it already satisfies the requirement.
    """
    result = script.pip_install_local(
        'pip', *install_args, use_module=use_module
    )
    assert expected_message in result.stdout, str(result)