diff options
author | Pradyun Gedam <pradyunsg@gmail.com> | 2018-10-23 19:20:46 +0530 |
---|---|---|
committer | Pradyun Gedam <pradyunsg@gmail.com> | 2018-10-23 19:20:46 +0530 |
commit | e19aceff40a3f1d91885c0e35ca86fc658f71c83 (patch) | |
tree | cfc135d7206b1f35ff20e1549134d31e348ea81a | |
parent | 5167495eb729f2f01ae0c4961b40dc1ca52ff828 (diff) | |
parent | 51819cc3d86f8c5bef91ede74672b72d34d7c228 (diff) | |
download | pip-e19aceff40a3f1d91885c0e35ca86fc658f71c83.tar.gz |
Merge branch 'master' into appveyor-rename
32 files changed, 516 insertions, 511 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index cfad2a52f..e4324b470 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,7 +18,8 @@ environment: install: - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - "python --version" - - "pip install certifi tox" + - "pip install --upgrade certifi tox tox-venv" + - "pip freeze --all" # Fix git SSL errors. - "python -m certifi >cacert.txt" - "set /p GIT_SSL_CAINFO=<cacert.txt" @@ -61,7 +62,7 @@ test_script: $env:TEMP = "T:\" $env:TMP = "T:\" if ($env:RUN_INTEGRATION_TESTS -eq "True") { - tox -e py -- -m integration -n 3 --duration=5 + tox -e py -- --use-venv -m integration -n 3 --duration=5 } else { tox -e py -- -m unit -n 3 diff --git a/.gitignore b/.gitignore index 31e69bc00..dfa41c022 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ htmlcov/ nosetests.xml coverage.xml *.cover +tests/data/common_wheels/ # Misc *~ diff --git a/.travis.yml b/.travis.yml index 058209841..c2af3efed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: python cache: pip dist: trusty +python: 3.6 stages: - primary @@ -12,8 +13,8 @@ jobs: - stage: primary env: TOXENV=docs - env: TOXENV=lint-py2 + python: 2.7 - env: TOXENV=lint-py3 - python: 3.6 - env: TOXENV=mypy - env: TOXENV=packaging # Latest CPython diff --git a/docs/html/development/getting-started.rst b/docs/html/development/getting-started.rst index 6a91b605d..96068c6d9 100644 --- a/docs/html/development/getting-started.rst +++ b/docs/html/development/getting-started.rst @@ -8,8 +8,8 @@ This document is meant to get you setup to work on pip and to act as a guide and reference to the the development setup. If you face any issues during this process, please `open an issue`_ about it on the issue tracker. -Development tools -================= +Development Environment +----------------------- pip uses :pypi:`tox` for testing against multiple different Python environments and ensuring reproducible environments for linting and building documentation. diff --git a/news/5839.bugfix b/news/5839.bugfix new file mode 100644 index 000000000..a9ce698d6 --- /dev/null +++ b/news/5839.bugfix @@ -0,0 +1 @@ +Fix crashes from unparseable requirements when checking installed packages. diff --git a/news/EFE4CACD-41B3-478D-926D-2069D76A6059.trivial b/news/EFE4CACD-41B3-478D-926D-2069D76A6059.trivial new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/news/EFE4CACD-41B3-478D-926D-2069D76A6059.trivial diff --git a/src/pip/_internal/commands/check.py b/src/pip/_internal/commands/check.py index 1be3ec21f..801cecc0b 100644 --- a/src/pip/_internal/commands/check.py +++ b/src/pip/_internal/commands/check.py @@ -16,7 +16,7 @@ class CheckCommand(Command): summary = 'Verify installed packages have compatible dependencies.' def run(self, options, args): - package_set = create_package_set_from_installed() + package_set, parsing_probs = create_package_set_from_installed() missing, conflicting = check_package_set(package_set) for project_name in missing: @@ -35,7 +35,7 @@ class CheckCommand(Command): project_name, version, req, dep_name, dep_version, ) - if missing or conflicting: + if missing or conflicting or parsing_probs: return 1 else: logger.info("No broken requirements found.") diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index 6fc178f9f..49a488730 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -472,7 +472,11 @@ class InstallCommand(RequirementCommand): ) def _warn_about_conflicts(self, to_install): - package_set, _dep_info = check_install_conflicts(to_install) + try: + package_set, _dep_info = check_install_conflicts(to_install) + except Exception: + logger.error("Error checking for conflicts.", exc_info=True) + return missing, conflicting = _dep_info # NOTE: There is some duplication here from pip check diff --git a/src/pip/_internal/operations/check.py b/src/pip/_internal/operations/check.py index 799257aea..0096fa429 100644 --- a/src/pip/_internal/operations/check.py +++ b/src/pip/_internal/operations/check.py @@ -1,14 +1,18 @@ """Validation of dependencies of packages """ +import logging from collections import namedtuple from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError from pip._internal.operations.prepare import make_abstract_dist from pip._internal.utils.misc import get_installed_distributions from pip._internal.utils.typing import MYPY_CHECK_RUNNING +logger = logging.getLogger(__name__) + if MYPY_CHECK_RUNNING: from pip._internal.req.req_install import InstallRequirement # noqa: F401 from typing import ( # noqa: F401 @@ -28,7 +32,7 @@ PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) def create_package_set_from_installed(**kwargs): - # type: (**Any) -> PackageSet + # type: (**Any) -> Tuple[PackageSet, bool] """Converts a list of distributions into a PackageSet. """ # Default to using all packages installed on the system @@ -36,10 +40,16 @@ def create_package_set_from_installed(**kwargs): kwargs = {"local_only": False, "skip": ()} package_set = {} + problems = False for dist in get_installed_distributions(**kwargs): name = canonicalize_name(dist.project_name) - package_set[name] = PackageDetails(dist.version, dist.requires()) - return package_set + try: + package_set[name] = PackageDetails(dist.version, dist.requires()) + except RequirementParseError as e: + # Don't crash on broken metadata + logging.warning("Error parsing requirements for %s: %s", name, e) + problems = True + return package_set, problems def check_package_set(package_set, should_ignore=None): @@ -95,7 +105,7 @@ def check_install_conflicts(to_install): installing given requirements """ # Start from the current state - package_set = create_package_set_from_installed() + package_set, _ = create_package_set_from_installed() # Install packages would_be_installed = _simulate_installation_of(to_install, package_set) diff --git a/src/pip/_internal/operations/freeze.py b/src/pip/_internal/operations/freeze.py index af484f2c1..d3cdefbfc 100644 --- a/src/pip/_internal/operations/freeze.py +++ b/src/pip/_internal/operations/freeze.py @@ -5,11 +5,11 @@ import logging import os import re -from pip._vendor import pkg_resources, six +from pip._vendor import six from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.pkg_resources import RequirementParseError -from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import BadCommand, InstallationError from pip._internal.req.constructors import ( install_req_from_editable, install_req_from_line, ) @@ -34,16 +34,6 @@ def freeze( if skip_regex: skip_match = re.compile(skip_regex).search - dependency_links = [] - - for dist in pkg_resources.working_set: - if dist.has_metadata('dependency_links.txt'): - dependency_links.extend( - dist.get_metadata_lines('dependency_links.txt') - ) - for link in find_links: - if '#egg=' in link: - dependency_links.append(link) for link in find_links: yield '-f %s' % link installations = {} @@ -51,10 +41,7 @@ def freeze( skip=(), user_only=user_only): try: - req = FrozenRequirement.from_dist( - dist, - dependency_links - ) + req = FrozenRequirement.from_dist(dist) except RequirementParseError: logger.warning( "Could not parse requirement: %s", @@ -156,56 +143,64 @@ def freeze( yield str(installation).rstrip() -class FrozenRequirement(object): - def __init__(self, name, req, editable, comments=()): - self.name = name - self.req = req - self.editable = editable - self.comments = comments +def get_requirement_info(dist): + """ + Compute and return values (req, editable, comments) for use in + FrozenRequirement.from_dist(). + """ + if not dist_is_editable(dist): + return (None, False, []) - @classmethod - def _init_args_from_dist(cls, dist, dependency_links): - """ - Compute and return arguments (req, editable, comments) to pass to - FrozenRequirement.__init__(). - - This method is for use in FrozenRequirement.from_dist(). - """ - location = os.path.normcase(os.path.abspath(dist.location)) - from pip._internal.vcs import vcs, get_src_requirement - if not dist_is_editable(dist): - req = dist.as_requirement() - return (req, False, []) + location = os.path.normcase(os.path.abspath(dist.location)) - vc_type = vcs.get_backend_type(location) + from pip._internal.vcs import vcs + vc_type = vcs.get_backend_type(location) - if not vc_type: - req = dist.as_requirement() - return (req, False, []) + if not vc_type: + return (None, False, []) - try: - req = get_src_requirement(vc_type, dist, location) - except InstallationError as exc: - logger.warning( - "Error when trying to get requirement for VCS system %s, " - "falling back to uneditable format", exc - ) - else: - if req is not None: - return (req, True, []) + try: + req = vc_type().get_src_requirement(dist, location) + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + vc_type.name, + ) + return (None, True, []) + except InstallationError as exc: logger.warning( - 'Could not determine repository location of %s', location + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc ) - comments = ['## !! Could not determine repository location'] - req = dist.as_requirement() + else: + if req is not None: + return (req, True, []) + + logger.warning( + 'Could not determine repository location of %s', location + ) + comments = ['## !! Could not determine repository location'] - return (req, False, comments) + return (None, False, comments) + + +class FrozenRequirement(object): + def __init__(self, name, req, editable, comments=()): + self.name = name + self.req = req + self.editable = editable + self.comments = comments @classmethod - def from_dist(cls, dist, dependency_links): - args = cls._init_args_from_dist(dist, dependency_links) - return cls(dist.project_name, *args) + def from_dist(cls, dist): + req, editable, comments = get_requirement_info(dist) + if req is None: + req = dist.as_requirement() + + return cls(dist.project_name, req, editable, comments=comments) def __str__(self): req = self.req diff --git a/src/pip/_internal/vcs/__init__.py b/src/pip/_internal/vcs/__init__.py index d34c8be0b..808c152f7 100644 --- a/src/pip/_internal/vcs/__init__.py +++ b/src/pip/_internal/vcs/__init__.py @@ -479,16 +479,3 @@ class VersionControl(object): the Git override checks that Git is actually available. """ return cls.is_repository_directory(location) - - -def get_src_requirement(vc_type, dist, location): - try: - return vc_type().get_src_requirement(dist, location) - except BadCommand: - logger.warning( - 'cannot determine version of editable source in %s ' - '(%s command not found in path)', - location, - vc_type.name, - ) - return dist.as_requirement() diff --git a/tests/conftest.py b/tests/conftest.py index 2656c3519..c86a91e3a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,4 @@ +import compileall import io import os import shutil @@ -6,9 +7,10 @@ import sys import pytest import six +from setuptools.wheel import Wheel import pip._internal -from tests.lib import SRC_DIR, TestData +from tests.lib import DATA_DIR, SRC_DIR, TestData from tests.lib.path import Path from tests.lib.scripttest import PipTestEnvironment from tests.lib.venv import VirtualEnvironment @@ -19,9 +21,11 @@ def pytest_addoption(parser): "--keep-tmpdir", action="store_true", default=False, help="keep temporary test directories" ) + parser.addoption("--use-venv", action="store_true", + help="use venv for virtual environment creation") -def pytest_collection_modifyitems(items): +def pytest_collection_modifyitems(config, items): for item in items: if not hasattr(item, 'module'): # e.g.: DoctestTextfile continue @@ -30,6 +34,16 @@ def pytest_collection_modifyitems(items): if item.get_marker('network') is not None and "CI" in os.environ: item.add_marker(pytest.mark.flaky(reruns=3)) + if six.PY3: + if (item.get_marker('incompatible_with_test_venv') and + config.getoption("--use-venv")): + item.add_marker(pytest.mark.skip( + 'Incompatible with test venv')) + if (item.get_marker('incompatible_with_venv') and + sys.prefix != sys.base_prefix): + item.add_marker(pytest.mark.skip( + 'Incompatible with venv')) + module_path = os.path.relpath( item.module.__file__, os.path.commonprefix([__file__, item.module.__file__]), @@ -56,6 +70,16 @@ def pytest_collection_modifyitems(items): ) +@pytest.fixture(scope='session') +def tmpdir_factory(request, tmpdir_factory): + """ Modified `tmpdir_factory` session fixture + that will automatically cleanup after itself. + """ + yield tmpdir_factory + if not request.config.getoption("--keep-tmpdir"): + tmpdir_factory.getbasetemp().remove(ignore_errors=True) + + @pytest.yield_fixture def tmpdir(request, tmpdir): """ @@ -163,63 +187,74 @@ def pip_src(tmpdir_factory): return pip_src +def _common_wheel_editable_install(tmpdir_factory, common_wheels, package): + wheel_candidates = list(common_wheels.glob('%s-*.whl' % package)) + assert len(wheel_candidates) == 1, wheel_candidates + install_dir = Path(str(tmpdir_factory.mktemp(package))) / 'install' + Wheel(wheel_candidates[0]).install_as_egg(install_dir) + (install_dir / 'EGG-INFO').rename(install_dir / '%s.egg-info' % package) + assert compileall.compile_dir(str(install_dir), quiet=1) + return install_dir + + +@pytest.fixture(scope='session') +def setuptools_install(tmpdir_factory, common_wheels): + return _common_wheel_editable_install(tmpdir_factory, + common_wheels, + 'setuptools') + + +@pytest.fixture(scope='session') +def wheel_install(tmpdir_factory, common_wheels): + return _common_wheel_editable_install(tmpdir_factory, + common_wheels, + 'wheel') + + +def install_egg_link(venv, project_name, egg_info_dir): + with open(venv.site / 'easy-install.pth', 'a') as fp: + fp.write(str(egg_info_dir.abspath) + '\n') + with open(venv.site / (project_name + '.egg-link'), 'w') as fp: + fp.write(str(egg_info_dir) + '\n.') + + @pytest.yield_fixture(scope='session') -def virtualenv_template(tmpdir_factory, pip_src): - tmpdir = Path(str(tmpdir_factory.mktemp('virtualenv'))) +def virtualenv_template(request, tmpdir_factory, pip_src, + setuptools_install, common_wheels): + + if six.PY3 and request.config.getoption('--use-venv'): + venv_type = 'venv' + else: + venv_type = 'virtualenv' + # Create the virtual environment - venv = VirtualEnvironment.create( - tmpdir.join("venv_orig"), - pip_source_dir=pip_src, - relocatable=True, - ) - # Fix `site.py`. - site_py = venv.lib / 'site.py' - with open(site_py) as fp: - site_contents = fp.read() - for pattern, replace in ( - ( - # Ensure `virtualenv.system_site_packages = True` (needed - # for testing `--user`) does not result in adding the real - # site-packages' directory to `sys.path`. - ( - '\ndef virtual_addsitepackages(known_paths):\n' - ), - ( - '\ndef virtual_addsitepackages(known_paths):\n' - ' return known_paths\n' - ), - ), - ( - # Fix sites ordering: user site must be added before system site. - ( - '\n paths_in_sys = addsitepackages(paths_in_sys)' - '\n paths_in_sys = addusersitepackages(paths_in_sys)\n' - ), - ( - '\n paths_in_sys = addusersitepackages(paths_in_sys)' - '\n paths_in_sys = addsitepackages(paths_in_sys)\n' - ), - ), - ): - assert pattern in site_contents - site_contents = site_contents.replace(pattern, replace) - with open(site_py, 'w') as fp: - fp.write(site_contents) - if sys.platform == 'win32': - # Work around setuptools' easy_install.exe - # not working properly after relocation. - for exe in os.listdir(venv.bin): - if exe.startswith('easy_install'): - (venv.bin / exe).remove() - with open(venv.bin / 'easy_install.bat', 'w') as fp: - fp.write('python.exe -m easy_install %*\n') + tmpdir = Path(str(tmpdir_factory.mktemp('virtualenv'))) + venv = VirtualEnvironment(tmpdir.join("venv_orig"), venv_type=venv_type) + + # Install setuptools and pip. + install_egg_link(venv, 'setuptools', setuptools_install) + pip_editable = Path(str(tmpdir_factory.mktemp('pip'))) / 'pip' + pip_src.copytree(pip_editable) + assert compileall.compile_dir(str(pip_editable), quiet=1) + subprocess.check_call([venv.bin / 'python', 'setup.py', '-q', 'develop'], + cwd=pip_editable) + + # Drop (non-relocatable) launchers. + for exe in os.listdir(venv.bin): + if not ( + exe.startswith('python') or + exe.startswith('libpy') # Don't remove libpypy-c.so... + ): + (venv.bin / exe).remove() + + # Enable user site packages. + venv.user_site_packages = True # Rename original virtualenv directory to make sure # it's not reused by mistake from one of the copies. venv_template = tmpdir / "venv_template" - os.rename(venv.location, venv_template) - yield venv_template - tmpdir.rmtree(noerrors=True) + venv.move(venv_template) + yield venv @pytest.yield_fixture @@ -231,10 +266,12 @@ def virtualenv(virtualenv_template, tmpdir, isolate): ``tests.lib.venv.VirtualEnvironment`` object. """ venv_location = tmpdir.join("workspace", "venv") - shutil.copytree(virtualenv_template, venv_location, symlinks=True) - venv = VirtualEnvironment(venv_location) - yield venv - venv_location.rmtree(noerrors=True) + yield VirtualEnvironment(venv_location, virtualenv_template) + + +@pytest.fixture +def with_wheel(virtualenv, wheel_install): + install_egg_link(virtualenv, 'wheel', wheel_install) @pytest.fixture @@ -250,7 +287,7 @@ def script(tmpdir, virtualenv): tmpdir.join("workspace"), # Tell the Test Environment where our virtualenv is located - virtualenv=virtualenv.location, + virtualenv=virtualenv, # Do not ignore hidden files, they need to be checked as well ignore_hidden=False, @@ -266,15 +303,9 @@ def script(tmpdir, virtualenv): @pytest.fixture(scope="session") -def common_wheels(tmpdir_factory): +def common_wheels(): """Provide a directory with latest setuptools and wheel wheels""" - wheels_dir = tmpdir_factory.mktemp('common_wheels') - subprocess.check_call([ - 'pip', 'download', 'wheel', 'setuptools', - '-d', str(wheels_dir), - ]) - yield wheels_dir - wheels_dir.remove(ignore_errors=True) + return DATA_DIR.join('common_wheels') @pytest.fixture diff --git a/tests/functional/test_check.py b/tests/functional/test_check.py index e0c65d0e7..45611e73e 100644 --- a/tests/functional/test_check.py +++ b/tests/functional/test_check.py @@ -222,3 +222,20 @@ def test_check_development_versions_are_also_considered(script): ) assert matches_expected_lines(result.stdout, expected_lines) assert result.returncode == 0 + + +def test_basic_check_broken_metadata(script): + # Create some corrupt metadata + dist_info_dir = script.site_packages_path / 'pkga-1.0.dist-info' + dist_info_dir.mkdir() + with open(dist_info_dir / 'METADATA', 'w') as f: + f.write('Metadata-Version: 2.1\n' + 'Name: pkga\n' + 'Version: 1.0\n' + 'Requires-Dist: pip; python_version == "3.4";extra == "test"\n' + ) + + result = script.pip('check', expect_error=True) + + assert 'Error parsing requirements' in result.stderr + assert result.returncode == 1 diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index 586a0a89b..bee64d319 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -3,29 +3,26 @@ import sys import pytest - -def test_completion_for_bash(script): - """ - Test getting completion for bash shell - """ - bash_completion = """\ +COMPLETION_FOR_SUPPORTED_SHELLS_TESTS = ( + ('bash', """\ _pip_completion() { COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\ COMP_CWORD=$COMP_CWORD \\ PIP_AUTO_COMPLETE=1 $1 ) ) } -complete -o default -F _pip_completion pip""" - - result = script.pip('completion', '--bash') - assert bash_completion in result.stdout, 'bash completion is wrong' - - -def test_completion_for_zsh(script): - """ - Test getting completion for zsh shell - """ - zsh_completion = """\ +complete -o default -F _pip_completion pip"""), + ('fish', """\ +function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) + set -lx PIP_AUTO_COMPLETE 1 + string split \\ -- (eval $COMP_WORDS[1]) +end +complete -fa "(__fish_complete_pip)" -c pip"""), + ('zsh', """\ function _pip_completion { local words cword read -Ac words @@ -34,29 +31,24 @@ function _pip_completion { COMP_CWORD=$(( cword-1 )) \\ PIP_AUTO_COMPLETE=1 $words[1] ) ) } -compctl -K _pip_completion pip""" - - result = script.pip('completion', '--zsh') - assert zsh_completion in result.stdout, 'zsh completion is wrong' +compctl -K _pip_completion pip"""), +) -def test_completion_for_fish(script): +@pytest.mark.parametrize( + 'shell, completion', + COMPLETION_FOR_SUPPORTED_SHELLS_TESTS, + ids=[t[0] for t in COMPLETION_FOR_SUPPORTED_SHELLS_TESTS], +) +def test_completion_for_supported_shells(script, pip_src, shell, completion): """ - Test getting completion for fish shell + Test getting completion for bash shell """ - fish_completion = """\ -function __fish_complete_pip - set -lx COMP_WORDS (commandline -o) "" - set -lx COMP_CWORD ( \\ - math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ - ) - set -lx PIP_AUTO_COMPLETE 1 - string split \\ -- (eval $COMP_WORDS[1]) -end -complete -fa "(__fish_complete_pip)" -c pip""" + # Re-install pip so we get the launchers. + script.pip_install_local('--no-build-isolation', pip_src) - result = script.pip('completion', '--fish') - assert fish_completion in result.stdout, 'fish completion is wrong' + result = script.pip('completion', '--' + shell, use_module=False) + assert completion in result.stdout, str(result.stdout) def test_completion_for_unknown_shell(script): diff --git a/tests/functional/test_freeze.py b/tests/functional/test_freeze.py index d51029281..bf6f058b8 100644 --- a/tests/functional/test_freeze.py +++ b/tests/functional/test_freeze.py @@ -576,7 +576,6 @@ def test_freeze_user(script, virtualenv, data): Testing freeze with --user, first we have to install some stuff. """ script.pip('download', 'setuptools', 'wheel', '-d', data.packages) - virtualenv.system_site_packages = True script.pip_install_local('--find-links', data.find_links, '--user', 'simple==2.0') script.pip_install_local('--find-links', data.find_links, diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index b10b78fa4..ab5d91cb2 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -26,15 +26,14 @@ def test_pep518_uses_build_env(script, data, common_wheels, command, variant): if variant == 'missing_setuptools': script.pip("uninstall", "-y", "setuptools") elif variant == 'bad_setuptools': - setuptools_init_path = script.site_packages_path.join( - "setuptools", "__init__.py") - with open(setuptools_init_path, 'a') as f: + setuptools_mod = script.site_packages_path.join("setuptools.py") + with open(setuptools_mod, 'a') as f: f.write('\nraise ImportError("toto")') else: raise ValueError(variant) script.pip( command, '--no-index', '-f', common_wheels, '-f', data.packages, - data.src.join("pep518-3.0"), use_module=True + data.src.join("pep518-3.0"), ) @@ -74,11 +73,8 @@ def test_pep518_allows_missing_requires(script, data, common_wheels): assert result.files_created -def test_pep518_with_user_pip(script, virtualenv, pip_src, - data, common_wheels): - virtualenv.system_site_packages = True - script.pip("install", "--ignore-installed", "--user", pip_src, - use_module=True) +def test_pep518_with_user_pip(script, pip_src, data, common_wheels): + script.pip("install", "--ignore-installed", "--user", pip_src) system_pip_dir = script.site_packages_path / 'pip' system_pip_dir.rmtree() system_pip_dir.mkdir() @@ -86,7 +82,7 @@ def test_pep518_with_user_pip(script, virtualenv, pip_src, fp.write('raise ImportError\n') script.pip( 'wheel', '--no-index', '-f', common_wheels, '-f', data.packages, - data.src.join("pep518-3.0"), use_module=True, + data.src.join("pep518-3.0"), ) @@ -96,7 +92,6 @@ def test_pep518_with_extra_and_markers(script, data, common_wheels): '-f', common_wheels, '-f', data.find_links, data.src.join("pep518_with_extra_and_markers-1.0"), - use_module=True, ) @@ -130,10 +125,12 @@ def test_pep518_forkbombs(script, data, common_wheels, command, package): @pytest.mark.network -def test_pip_second_command_line_interface_works(script, data): +def test_pip_second_command_line_interface_works(script, data, pip_src): """ Check if ``pip<PYVERSION>`` commands behaves equally """ + # Re-install pip so we get the launchers. + script.pip_install_local('--no-build-isolation', pip_src) # On old versions of Python, urllib3/requests will raise a warning about # the lack of an SSLContext. kwargs = {} @@ -226,10 +223,8 @@ def test_basic_install_editable_from_git(script, tmpdir): _test_install_editable_from_git(script, tmpdir) -@pytest.mark.network def test_install_editable_from_git_autobuild_wheel( - script, tmpdir, common_wheels): - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) + script, tmpdir, with_wheel): _test_install_editable_from_git(script, tmpdir) @@ -742,14 +737,11 @@ def test_install_nonlocal_compatible_wheel_path(script, data): assert result.returncode == ERROR -def test_install_with_target_and_scripts_no_warning(script, common_wheels): +def test_install_with_target_and_scripts_no_warning(script, with_wheel): """ Test that installing with --target does not trigger the "script not in PATH" warning (issue #5201) """ - # We need to have wheel installed so that the project builds via a wheel, - # which is the only execution path that has the script warning. - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) target_dir = script.scratch_path / 'target' pkga_path = script.scratch_path / 'pkga' pkga_path.mkdir() @@ -1093,23 +1085,13 @@ def test_install_topological_sort(script, data): assert order1 in res or order2 in res, res -@pytest.mark.network -def test_install_wheel_broken(script, data, common_wheels): - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) - res = script.pip( - 'install', '--no-index', '-f', data.find_links, '-f', common_wheels, - 'wheelbroken', - expect_stderr=True) +def test_install_wheel_broken(script, with_wheel): + res = script.pip_install_local('wheelbroken', expect_stderr=True) assert "Successfully installed wheelbroken-0.1" in str(res), str(res) -@pytest.mark.network -def test_cleanup_after_failed_wheel(script, data, common_wheels): - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) - res = script.pip( - 'install', '--no-index', '-f', data.find_links, '-f', common_wheels, - 'wheelbrokenafter', - expect_stderr=True) +def test_cleanup_after_failed_wheel(script, with_wheel): + res = script.pip_install_local('wheelbrokenafter', expect_stderr=True) # One of the effects of not cleaning up is broken scripts: script_py = script.bin_path / "script.py" assert script_py.exists, script_py @@ -1119,8 +1101,7 @@ def test_cleanup_after_failed_wheel(script, data, common_wheels): assert "Running setup.py clean for wheelbrokenafter" in str(res), str(res) -@pytest.mark.network -def test_install_builds_wheels(script, data, common_wheels): +def test_install_builds_wheels(script, data, with_wheel): # We need to use a subprocess to get the right value on Windows. res = script.run('python', '-c', ( 'from pip._internal.utils import appdirs; ' @@ -1130,10 +1111,9 @@ def test_install_builds_wheels(script, data, common_wheels): # NB This incidentally tests a local tree + tarball inputs # see test_install_editable_from_git_autobuild_wheel for editable # vcs coverage. - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) to_install = data.packages.join('requires_wheelbroken_upper') res = script.pip( - 'install', '--no-index', '-f', data.find_links, '-f', common_wheels, + 'install', '--no-index', '-f', data.find_links, to_install, expect_stderr=True) expected = ("Successfully installed requires-wheelbroken-upper-0" " upper-2.0 wheelbroken-0.1") @@ -1162,14 +1142,10 @@ def test_install_builds_wheels(script, data, common_wheels): ] -@pytest.mark.network -def test_install_no_binary_disables_building_wheels( - script, data, common_wheels): - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) +def test_install_no_binary_disables_building_wheels(script, data, with_wheel): to_install = data.packages.join('requires_wheelbroken_upper') res = script.pip( 'install', '--no-index', '--no-binary=upper', '-f', data.find_links, - '-f', common_wheels, to_install, expect_stderr=True) expected = ("Successfully installed requires-wheelbroken-upper-0" " upper-2.0 wheelbroken-0.1") @@ -1188,12 +1164,10 @@ def test_install_no_binary_disables_building_wheels( assert "Running setup.py install for upper" in str(res), str(res) -@pytest.mark.network -def test_install_no_binary_disables_cached_wheels(script, data, common_wheels): - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) +def test_install_no_binary_disables_cached_wheels(script, data, with_wheel): # Seed the cache script.pip( - 'install', '--no-index', '-f', data.find_links, '-f', common_wheels, + 'install', '--no-index', '-f', data.find_links, 'upper') script.pip('uninstall', 'upper', '-y') res = script.pip( @@ -1246,7 +1220,6 @@ def test_double_install(script): Test double install passing with two same version requirements """ result = script.pip('install', 'pip', 'pip', - use_module=True, expect_error=False) msg = "Double requirement given: pip (already in pip, name='pip')" assert msg not in result.stderr @@ -1262,7 +1235,7 @@ def test_double_install_fail(script): assert msg in result.stderr -def test_install_incompatible_python_requires(script, common_wheels): +def test_install_incompatible_python_requires(script): script.scratch_path.join("pkga").mkdir() pkga_path = script.scratch_path / 'pkga' pkga_path.join("setup.py").write(textwrap.dedent(""" @@ -1271,16 +1244,12 @@ def test_install_incompatible_python_requires(script, common_wheels): python_requires='<1.0', version='0.1') """)) - script.pip( - 'install', 'setuptools>24.2', # This should not be needed - '--no-index', '-f', common_wheels, - ) result = script.pip('install', pkga_path, expect_error=True) assert ("pkga requires Python '<1.0' " "but the running Python is ") in result.stderr, str(result) -def test_install_incompatible_python_requires_editable(script, common_wheels): +def test_install_incompatible_python_requires_editable(script): script.scratch_path.join("pkga").mkdir() pkga_path = script.scratch_path / 'pkga' pkga_path.join("setup.py").write(textwrap.dedent(""" @@ -1289,18 +1258,13 @@ def test_install_incompatible_python_requires_editable(script, common_wheels): python_requires='<1.0', version='0.1') """)) - script.pip( - 'install', 'setuptools>24.2', # This should not be needed - '--no-index', '-f', common_wheels, - ) result = script.pip( 'install', '--editable=%s' % pkga_path, expect_error=True) assert ("pkga requires Python '<1.0' " "but the running Python is ") in result.stderr, str(result) -@pytest.mark.network -def test_install_incompatible_python_requires_wheel(script, common_wheels): +def test_install_incompatible_python_requires_wheel(script, with_wheel): script.scratch_path.join("pkga").mkdir() pkga_path = script.scratch_path / 'pkga' pkga_path.join("setup.py").write(textwrap.dedent(""" @@ -1309,11 +1273,6 @@ def test_install_incompatible_python_requires_wheel(script, common_wheels): python_requires='<1.0', version='0.1') """)) - script.pip( - 'install', 'setuptools>24.2', # This should not be needed - '--no-index', '-f', common_wheels, - ) - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) 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', @@ -1322,7 +1281,7 @@ def test_install_incompatible_python_requires_wheel(script, common_wheels): "but the running Python is ") in result.stderr -def test_install_compatible_python_requires(script, common_wheels): +def test_install_compatible_python_requires(script): script.scratch_path.join("pkga").mkdir() pkga_path = script.scratch_path / 'pkga' pkga_path.join("setup.py").write(textwrap.dedent(""" @@ -1331,10 +1290,6 @@ def test_install_compatible_python_requires(script, common_wheels): python_requires='>1.0', version='0.1') """)) - script.pip( - 'install', 'setuptools>24.2', # This should not be needed - '--no-index', '-f', common_wheels, - ) res = script.pip('install', pkga_path, expect_error=True) assert "Successfully installed pkga-0.1" in res.stdout, res diff --git a/tests/functional/test_install_config.py b/tests/functional/test_install_config.py index bf2516685..e4ad7e9f2 100644 --- a/tests/functional/test_install_config.py +++ b/tests/functional/test_install_config.py @@ -199,10 +199,8 @@ def test_options_from_venv_config(script, virtualenv): ) -@pytest.mark.network def test_install_no_binary_via_config_disables_cached_wheels( - script, data, common_wheels): - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) + script, data, with_wheel): config_file = tempfile.NamedTemporaryFile(mode='wt', delete=False) try: script.environ['PIP_CONFIG_FILE'] = config_file.name diff --git a/tests/functional/test_install_reqs.py b/tests/functional/test_install_reqs.py index 582e17ed2..ff000fcd0 100644 --- a/tests/functional/test_install_reqs.py +++ b/tests/functional/test_install_reqs.py @@ -223,12 +223,8 @@ def test_install_local_with_subdirectory(script): result.assert_installed('version_subpkg.py', editable=False) -@pytest.mark.network def test_wheel_user_with_prefix_in_pydistutils_cfg( - script, data, virtualenv, common_wheels): - # Make sure wheel is available in the virtualenv - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) - virtualenv.system_site_packages = True + script, data, with_wheel): if os.name == 'posix': user_filename = ".pydistutils.cfg" else: @@ -242,7 +238,7 @@ def test_wheel_user_with_prefix_in_pydistutils_cfg( result = script.pip( 'install', '--user', '--no-index', - '-f', data.find_links, '-f', common_wheels, + '-f', data.find_links, 'requiresupper') # Check that we are really installing a wheel assert 'Running setup.py install for requiresupper' not in result.stdout @@ -353,9 +349,8 @@ def test_constrained_to_url_install_same_url(script, data): in result.stdout), str(result) -@pytest.mark.network def test_double_install_spurious_hash_mismatch( - script, tmpdir, data, common_wheels): + script, tmpdir, data, with_wheel): """Make sure installing the same hashed sdist twice doesn't throw hash mismatch errors. @@ -366,13 +361,12 @@ def test_double_install_spurious_hash_mismatch( """ # Install wheel package, otherwise, it won't try to build wheels. - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) with requirements_file('simple==1.0 --hash=sha256:393043e672415891885c9a2a' '0929b1af95fb866d6ca016b42d2e6ce53619b653', tmpdir) as reqs_file: # Install a package (and build its wheel): result = script.pip_install_local( - '--find-links', data.find_links, '-f', common_wheels, + '--find-links', data.find_links, '-r', reqs_file.abspath, expect_error=False) assert 'Successfully installed simple-1.0' in str(result) @@ -382,7 +376,7 @@ def test_double_install_spurious_hash_mismatch( # Then install it again. We should not hit a hash mismatch, and the # package should install happily. result = script.pip_install_local( - '--find-links', data.find_links, '-f', common_wheels, + '--find-links', data.find_links, '-r', reqs_file.abspath, expect_error=False) assert 'Successfully installed simple-1.0' in str(result) diff --git a/tests/functional/test_install_user.py b/tests/functional/test_install_user.py index 09edcb965..17d09768b 100644 --- a/tests/functional/test_install_user.py +++ b/tests/functional/test_install_user.py @@ -1,46 +1,36 @@ """ tests specific to "pip install --user" """ -import os import textwrap from os.path import curdir, isdir, isfile import pytest -from pip._internal.utils.compat import cache_from_source, uses_pycache from tests.lib import pyversion from tests.lib.local_repos import local_checkout -def _patch_dist_in_site_packages(script): - sitecustomize_path = script.lib_path.join("sitecustomize.py") - sitecustomize_path.write(textwrap.dedent(""" +def _patch_dist_in_site_packages(virtualenv): + # Since the tests are run from a virtualenv, and to avoid the "Will not + # install to the usersite because it will lack sys.path precedence..." + # error: Monkey patch `pip._internal.req.req_install.dist_in_site_packages` + # so it's possible to install a conflicting distribution in the user site. + virtualenv.sitecustomize = textwrap.dedent(""" def dist_in_site_packages(dist): return False from pip._internal.req import req_install req_install.dist_in_site_packages = dist_in_site_packages - """)) - - # Caught py32 with an outdated __pycache__ file after a sitecustomize - # update (after python should have updated it) so will delete the cache - # file to be sure - # See: https://github.com/pypa/pip/pull/893#issuecomment-16426701 - if uses_pycache: - cache_path = cache_from_source(sitecustomize_path) - if os.path.isfile(cache_path): - os.remove(cache_path) + """) class Tests_UserSite: @pytest.mark.network - def test_reset_env_system_site_packages_usersite(self, script, virtualenv): + def test_reset_env_system_site_packages_usersite(self, script): """ - reset_env(system_site_packages=True) produces env where a --user - install can be found using pkg_resources + Check user site works as expected. """ - virtualenv.system_site_packages = True script.pip('install', '--user', 'INITools==0.2') result = script.run( 'python', '-c', @@ -52,12 +42,11 @@ class Tests_UserSite: @pytest.mark.network def test_install_subversion_usersite_editable_with_distribute( - self, script, virtualenv, tmpdir): + self, script, tmpdir): """ Test installing current directory ('.') into usersite after installing distribute """ - virtualenv.system_site_packages = True result = script.pip( 'install', '--user', '-e', '%s#egg=initools' % @@ -68,15 +57,11 @@ class Tests_UserSite: ) result.assert_installed('INITools', use_user_site=True) - @pytest.mark.network def test_install_from_current_directory_into_usersite( - self, script, virtualenv, data, common_wheels): + self, script, data, with_wheel): """ Test installing current directory ('.') into usersite """ - virtualenv.system_site_packages = True - script.pip("install", "wheel", '--no-index', '-f', common_wheels) - run_from = data.packages.join("FSPkg") result = script.pip( 'install', '-vvv', '--user', curdir, @@ -92,10 +77,15 @@ class Tests_UserSite: ) assert dist_info_folder in result.files_created - def test_install_user_venv_nositepkgs_fails(self, script, data): + @pytest.mark.incompatible_with_test_venv + def test_install_user_venv_nositepkgs_fails(self, virtualenv, + script, data): """ user install in virtualenv (with no system packages) fails with message """ + # We can't use PYTHONNOUSERSITE, as it's not + # honoured by virtualenv's custom site.py. + virtualenv.user_site_packages = False run_from = data.packages.join("FSPkg") result = script.pip( 'install', '--user', curdir, @@ -108,11 +98,10 @@ class Tests_UserSite: ) @pytest.mark.network - def test_install_user_conflict_in_usersite(self, script, virtualenv): + def test_install_user_conflict_in_usersite(self, script): """ Test user install with conflict in usersite updates usersite. """ - virtualenv.system_site_packages = True script.pip('install', '--user', 'INITools==0.3', '--no-binary=:all:') @@ -132,26 +121,12 @@ class Tests_UserSite: assert not isfile(initools_v3_file), initools_v3_file @pytest.mark.network - def test_install_user_conflict_in_globalsite(self, script, virtualenv): + def test_install_user_conflict_in_globalsite(self, virtualenv, script): """ Test user install with conflict in global site ignores site and installs to usersite """ - # the test framework only supports testing using virtualenvs - # the sys.path ordering for virtualenvs with --system-site-packages is - # this: virtualenv-site, user-site, global-site - # this test will use 2 modifications to simulate the - # user-site/global-site relationship - # 1) a monkey patch which will make it appear INITools==0.2 is not in - # the virtualenv site if we don't patch this, pip will return an - # installation error: "Will not install to the usersite because it - # will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence - # over the virtualenv site - - virtualenv.system_site_packages = True - script.environ["PYTHONPATH"] = script.base_path / script.user_site - _patch_dist_in_site_packages(script) + _patch_dist_in_site_packages(virtualenv) script.pip('install', 'INITools==0.2', '--no-binary=:all:') @@ -176,26 +151,12 @@ class Tests_UserSite: assert isdir(initools_folder) @pytest.mark.network - def test_upgrade_user_conflict_in_globalsite(self, script, virtualenv): + def test_upgrade_user_conflict_in_globalsite(self, virtualenv, script): """ Test user install/upgrade with conflict in global site ignores site and installs to usersite """ - # the test framework only supports testing using virtualenvs - # the sys.path ordering for virtualenvs with --system-site-packages is - # this: virtualenv-site, user-site, global-site - # this test will use 2 modifications to simulate the - # user-site/global-site relationship - # 1) a monkey patch which will make it appear INITools==0.2 is not in - # the virtualenv site if we don't patch this, pip will return an - # installation error: "Will not install to the usersite because it - # will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence - # over the virtualenv site - - virtualenv.system_site_packages = True - script.environ["PYTHONPATH"] = script.base_path / script.user_site - _patch_dist_in_site_packages(script) + _patch_dist_in_site_packages(virtualenv) script.pip('install', 'INITools==0.2', '--no-binary=:all:') result2 = script.pip( @@ -220,26 +181,12 @@ class Tests_UserSite: @pytest.mark.network def test_install_user_conflict_in_globalsite_and_usersite( - self, script, virtualenv): + self, virtualenv, script): """ Test user install with conflict in globalsite and usersite ignores global site and updates usersite. """ - # the test framework only supports testing using virtualenvs. - # the sys.path ordering for virtualenvs with --system-site-packages is - # this: virtualenv-site, user-site, global-site. - # this test will use 2 modifications to simulate the - # user-site/global-site relationship - # 1) a monkey patch which will make it appear INITools==0.2 is not in - # the virtualenv site if we don't patch this, pip will return an - # installation error: "Will not install to the usersite because it - # will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence - # over the virtualenv site - - virtualenv.system_site_packages = True - script.environ["PYTHONPATH"] = script.base_path / script.user_site - _patch_dist_in_site_packages(script) + _patch_dist_in_site_packages(virtualenv) script.pip('install', 'INITools==0.2', '--no-binary=:all:') script.pip('install', '--user', 'INITools==0.3', '--no-binary=:all:') @@ -270,12 +217,11 @@ class Tests_UserSite: @pytest.mark.network def test_install_user_in_global_virtualenv_with_conflict_fails( - self, script, virtualenv): + self, script): """ Test user install in --system-site-packages virtualenv with conflict in site fails. """ - virtualenv.system_site_packages = True script.pip('install', 'INITools==0.2') diff --git a/tests/functional/test_install_wheel.py b/tests/functional/test_install_wheel.py index 58b9d2bc1..9937d5a0a 100644 --- a/tests/functional/test_install_wheel.py +++ b/tests/functional/test_install_wheel.py @@ -114,12 +114,10 @@ def test_install_from_wheel_with_headers(script, data): result.stdout) -@pytest.mark.network -def test_install_wheel_with_target(script, data, common_wheels): +def test_install_wheel_with_target(script, data, with_wheel): """ Test installing a wheel using pip install --target """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) target_dir = script.scratch_path / 'target' result = script.pip( 'install', 'simple.dist==0.1', '-t', target_dir, @@ -130,8 +128,7 @@ def test_install_wheel_with_target(script, data, common_wheels): ) -@pytest.mark.network -def test_install_wheel_with_target_and_data_files(script, data, common_wheels): +def test_install_wheel_with_target_and_data_files(script, data, with_wheel): """ Test for issue #4092. It will be checked that a data_files specification in setup.py is handled correctly when a wheel is installed with the --target @@ -150,7 +147,6 @@ def test_install_wheel_with_target_and_data_files(script, data, common_wheels): ] ) """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) target_dir = script.scratch_path / 'prjwithdatafile' package = data.packages.join("prjwithdatafile-1.0-py2.py3-none-any.whl") result = script.pip('install', package, @@ -234,13 +230,10 @@ def test_wheel_record_lines_in_deterministic_order(script, data): assert record_lines == sorted(record_lines) -@pytest.mark.network -def test_install_user_wheel(script, virtualenv, data, common_wheels): +def test_install_user_wheel(script, data, with_wheel): """ Test user install from wheel (that has a script) """ - virtualenv.system_site_packages = True - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) result = script.pip( 'install', 'has.script==1.0', '--user', '--no-index', '--find-links=' + data.find_links, diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py index 1f12d91e0..c376049c5 100644 --- a/tests/functional/test_list.py +++ b/tests/functional/test_list.py @@ -99,12 +99,11 @@ def test_local_columns_flag(script, data): @pytest.mark.network -def test_user_flag(script, data, virtualenv): +def test_user_flag(script, data): """ Test the behavior of --user flag in the list command """ - virtualenv.system_site_packages = True script.pip('download', 'setuptools', 'wheel', '-d', data.packages) script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') script.pip('install', '-f', data.find_links, '--no-index', @@ -116,12 +115,11 @@ def test_user_flag(script, data, virtualenv): @pytest.mark.network -def test_user_columns_flag(script, data, virtualenv): +def test_user_columns_flag(script, data): """ Test the behavior of --user --format=columns flags in the list command """ - virtualenv.system_site_packages = True script.pip('download', 'setuptools', 'wheel', '-d', data.packages) script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') script.pip('install', '-f', data.find_links, '--no-index', diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py index 7fd47b17e..2748e2f7a 100644 --- a/tests/functional/test_uninstall.py +++ b/tests/functional/test_uninstall.py @@ -67,7 +67,7 @@ def test_basic_uninstall_with_scripts(script): Uninstall an easy_installed package with scripts. """ - result = script.run('easy_install', 'PyLogo', expect_stderr=True) + result = script.easy_install('PyLogo', expect_stderr=True) easy_install_pth = script.site_packages / 'easy-install.pth' pylogo = sys.platform == 'win32' and 'pylogo' or 'PyLogo' assert(pylogo in result.files_updated[easy_install_pth].bytes) @@ -85,7 +85,8 @@ def test_uninstall_easy_install_after_import(script): Uninstall an easy_installed package after it's been imported """ - result = script.run('easy_install', 'INITools==0.2', expect_stderr=True) + result = script.easy_install('--always-unzip', 'INITools==0.2', + expect_stderr=True) # the import forces the generation of __pycache__ if the version of python # supports it script.run('python', '-c', "import initools") @@ -108,8 +109,8 @@ def test_uninstall_trailing_newline(script): lacks a trailing newline """ - script.run('easy_install', 'INITools==0.2', expect_stderr=True) - script.run('easy_install', 'PyLogo', expect_stderr=True) + script.easy_install('INITools==0.2', expect_stderr=True) + script.easy_install('PyLogo', expect_stderr=True) easy_install_pth = script.site_packages_path / 'easy-install.pth' # trim trailing newline from easy-install.pth @@ -269,9 +270,7 @@ def test_uninstall_easy_installed_console_scripts(script): """ Test uninstalling package with console_scripts that is easy_installed. """ - args = ['easy_install'] - args.append('discover') - result = script.run(*args, **{"expect_stderr": True}) + result = script.easy_install('discover', expect_error=True) assert script.bin / 'discover' + script.exe in result.files_created, ( sorted(result.files_created.keys()) ) diff --git a/tests/functional/test_uninstall_user.py b/tests/functional/test_uninstall_user.py index eaa213f77..6cddacdbc 100644 --- a/tests/functional/test_uninstall_user.py +++ b/tests/functional/test_uninstall_user.py @@ -12,35 +12,20 @@ from tests.lib import assert_all_changes, pyversion class Tests_UninstallUserSite: @pytest.mark.network - def test_uninstall_from_usersite(self, script, virtualenv): + def test_uninstall_from_usersite(self, script): """ Test uninstall from usersite """ - virtualenv.system_site_packages = True result1 = script.pip('install', '--user', 'INITools==0.3') result2 = script.pip('uninstall', '-y', 'INITools') assert_all_changes(result1, result2, [script.venv / 'build', 'cache']) def test_uninstall_from_usersite_with_dist_in_global_site( - self, script, virtualenv): + self, virtualenv, script): """ Test uninstall from usersite (with same dist in global site) """ - # the test framework only supports testing using virtualenvs. - # the sys.path ordering for virtualenvs with --system-site-packages is - # this: virtualenv-site, user-site, global-site. - # this test will use 2 modifications to simulate the - # user-site/global-site relationship - # 1) a monkey patch which will make it appear piptestpackage is not in - # the virtualenv site if we don't patch this, pip will return an - # installation error: "Will not install to the usersite because it - # will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite has sys.path precedence - # over the virtualenv site - - virtualenv.system_site_packages = True - script.environ["PYTHONPATH"] = script.base_path / script.user_site - _patch_dist_in_site_packages(script) + _patch_dist_in_site_packages(virtualenv) script.pip_install_local('pip-test-package==0.1', '--no-binary=:all:') @@ -62,11 +47,10 @@ class Tests_UninstallUserSite: ) assert isdir(egg_info_folder) - def test_uninstall_editable_from_usersite(self, script, virtualenv, data): + def test_uninstall_editable_from_usersite(self, script, data): """ Test uninstall editable local user install """ - virtualenv.system_site_packages = True script.user_site_path.makedirs() # install diff --git a/tests/functional/test_wheel.py b/tests/functional/test_wheel.py index fee5c7b38..0e1e2e54a 100644 --- a/tests/functional/test_wheel.py +++ b/tests/functional/test_wheel.py @@ -9,34 +9,34 @@ from pip._internal.locations import write_delete_marker_file from tests.lib import pyversion -def test_wheel_exit_status_code_when_no_requirements(script, common_wheels): +@pytest.fixture(autouse=True) +def auto_with_wheel(with_wheel): + pass + + +def test_wheel_exit_status_code_when_no_requirements(script): """ Test wheel exit status code when no requirements specified """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) result = script.pip('wheel', expect_error=True) assert "You must give at least one requirement to wheel" in result.stderr assert result.returncode == ERROR -def test_wheel_exit_status_code_when_blank_requirements_file( - script, common_wheels): +def test_wheel_exit_status_code_when_blank_requirements_file(script): """ Test wheel exit status code when blank requirements file specified """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) script.scratch_path.join("blank.txt").write("\n") script.pip('wheel', '-r', 'blank.txt') -@pytest.mark.network -def test_pip_wheel_success(script, data, common_wheels): +def test_pip_wheel_success(script, data): """ Test 'pip wheel' success. """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) result = script.pip( - 'wheel', '--no-index', '-f', data.find_links, '-f', common_wheels, + 'wheel', '--no-index', '-f', data.find_links, 'simple==3.0', ) wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion[0] @@ -45,12 +45,10 @@ def test_pip_wheel_success(script, data, common_wheels): assert "Successfully built simple" in result.stdout, result.stdout -@pytest.mark.network -def test_basic_pip_wheel_downloads_wheels(script, data, common_wheels): +def test_basic_pip_wheel_downloads_wheels(script, data): """ Test 'pip wheel' downloads wheels """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) result = script.pip( 'wheel', '--no-index', '-f', data.find_links, 'simple.dist', ) @@ -60,27 +58,23 @@ def test_basic_pip_wheel_downloads_wheels(script, data, common_wheels): assert "Saved" in result.stdout, result.stdout -@pytest.mark.network -def test_pip_wheel_builds_when_no_binary_set(script, data, common_wheels): - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) +def test_pip_wheel_builds_when_no_binary_set(script, data): data.packages.join('simple-3.0-py2.py3-none-any.whl').touch() # Check that the wheel package is ignored res = script.pip( 'wheel', '--no-index', '--no-binary', ':all:', - '-f', data.find_links, '-f', common_wheels, + '-f', data.find_links, 'simple==3.0') assert "Running setup.py bdist_wheel for simple" in str(res), str(res) -@pytest.mark.network -def test_pip_wheel_builds_editable_deps(script, data, common_wheels): +def test_pip_wheel_builds_editable_deps(script, data): """ Test 'pip wheel' finds and builds dependencies of editables """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) editable_path = os.path.join(data.src, 'requires_simple') result = script.pip( - 'wheel', '--no-index', '-f', data.find_links, '-f', common_wheels, + 'wheel', '--no-index', '-f', data.find_links, '-e', editable_path ) wheel_file_name = 'simple-1.0-py%s-none-any.whl' % pyversion[0] @@ -88,15 +82,13 @@ def test_pip_wheel_builds_editable_deps(script, data, common_wheels): assert wheel_file_path in result.files_created, result.stdout -@pytest.mark.network -def test_pip_wheel_builds_editable(script, data, common_wheels): +def test_pip_wheel_builds_editable(script, data): """ Test 'pip wheel' builds an editable package """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) editable_path = os.path.join(data.src, 'simplewheel-1.0') result = script.pip( - 'wheel', '--no-index', '-f', data.find_links, '-f', common_wheels, + 'wheel', '--no-index', '-f', data.find_links, '-e', editable_path ) wheel_file_name = 'simplewheel-1.0-py%s-none-any.whl' % pyversion[0] @@ -104,14 +96,12 @@ def test_pip_wheel_builds_editable(script, data, common_wheels): assert wheel_file_path in result.files_created, result.stdout -@pytest.mark.network -def test_pip_wheel_fail(script, data, common_wheels): +def test_pip_wheel_fail(script, data): """ Test 'pip wheel' failure. """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) result = script.pip( - 'wheel', '--no-index', '-f', data.find_links, '-f', common_wheels, + 'wheel', '--no-index', '-f', data.find_links, 'wheelbroken==0.1', expect_error=True, ) @@ -126,17 +116,14 @@ def test_pip_wheel_fail(script, data, common_wheels): assert result.returncode != 0 -@pytest.mark.network -def test_no_clean_option_blocks_cleaning_after_wheel( - script, data, common_wheels): +def test_no_clean_option_blocks_cleaning_after_wheel(script, data): """ Test --no-clean option blocks cleaning after wheel build """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) build = script.venv_path / 'build' result = script.pip( 'wheel', '--no-clean', '--no-index', '--build', build, - '--find-links=%s' % data.find_links, '-f', common_wheels, + '--find-links=%s' % data.find_links, 'simple', expect_temp=True, ) @@ -144,16 +131,14 @@ def test_no_clean_option_blocks_cleaning_after_wheel( assert exists(build), "build/simple should still exist %s" % str(result) -@pytest.mark.network -def test_pip_wheel_source_deps(script, data, common_wheels): +def test_pip_wheel_source_deps(script, data): """ Test 'pip wheel' finds and builds source archive dependencies of wheels """ # 'requires_source' is a wheel that depends on the 'source' project - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) result = script.pip( - 'wheel', '--no-index', '-f', data.find_links, '-f', common_wheels, + 'wheel', '--no-index', '-f', data.find_links, 'requires_source', ) wheel_file_name = 'source-1.0-py%s-none-any.whl' % pyversion[0] @@ -162,16 +147,12 @@ def test_pip_wheel_source_deps(script, data, common_wheels): assert "Successfully built source" in result.stdout, result.stdout -@pytest.mark.network -def test_pip_wheel_fail_cause_of_previous_build_dir( - script, data, common_wheels): +def test_pip_wheel_fail_cause_of_previous_build_dir(script, data): """ Test when 'pip wheel' tries to install a package that has a previous build directory """ - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) - # Given that I have a previous build dir of the `simple` package build = script.venv_path / 'build' / 'simple' os.makedirs(build) @@ -189,19 +170,15 @@ def test_pip_wheel_fail_cause_of_previous_build_dir( assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, result -@pytest.mark.network -def test_wheel_package_with_latin1_setup(script, data, common_wheels): +def test_wheel_package_with_latin1_setup(script, data): """Create a wheel from a package with latin-1 encoded setup.py.""" - script.pip('install', 'wheel', '--no-index', '-f', common_wheels) pkg_to_wheel = data.packages.join("SetupPyLatin1") result = script.pip('wheel', pkg_to_wheel) assert 'Successfully built SetupPyUTF8' in result.stdout -@pytest.mark.network def test_pip_wheel_with_pep518_build_reqs(script, data, common_wheels): - script.pip_install_local('-f', common_wheels, 'wheel') result = script.pip('wheel', '--no-index', '-f', data.find_links, '-f', common_wheels, 'pep518==3.0',) wheel_file_name = 'pep518-3.0-py%s-none-any.whl' % pyversion[0] @@ -211,10 +188,8 @@ def test_pip_wheel_with_pep518_build_reqs(script, data, common_wheels): assert "Installing build dependencies" in result.stdout, result.stdout -@pytest.mark.network -def test_pip_wheel_with_pep518_build_reqs_no_isolation(script, data, - common_wheels): - script.pip_install_local('-f', common_wheels, 'wheel', 'simplewheel==2.0') +def test_pip_wheel_with_pep518_build_reqs_no_isolation(script, data): + script.pip_install_local('simplewheel==2.0') result = script.pip( 'wheel', '--no-index', '-f', data.find_links, '--no-build-isolation', 'pep518==3.0', @@ -226,9 +201,7 @@ def test_pip_wheel_with_pep518_build_reqs_no_isolation(script, data, assert "Installing build dependencies" not in result.stdout, result.stdout -@pytest.mark.network def test_pip_wheel_with_user_set_in_config(script, data, common_wheels): - script.pip_install_local('-f', common_wheels, 'wheel') config_file = script.scratch_path / 'pip.conf' script.environ['PIP_CONFIG_FILE'] = str(config_file) config_file.write("[install]\nuser = true") diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 5a00fb425..bd8903df6 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -10,9 +10,7 @@ import shutil import subprocess import pytest -import scripttest -import six -import virtualenv +from scripttest import FoundDir, TestFileEnvironment from tests.lib.path import Path, curdir @@ -40,15 +38,6 @@ def path_to_url(path): return 'file://' + url -# workaround for https://github.com/pypa/virtualenv/issues/306 -def virtualenv_lib_path(venv_home, venv_lib): - if not hasattr(sys, "pypy_version_info"): - return venv_lib - version_fmt = '{0}' if six.PY3 else '{0}.{1}' - version_dir = version_fmt.format(*sys.version_info) - return os.path.join(venv_home, 'lib-python', version_dir) - - def create_file(path, contents=None): """Create a file on the path, with the given contents """ @@ -259,7 +248,7 @@ class TestPipResult(object): ) -class PipTestEnvironment(scripttest.TestFileEnvironment): +class PipTestEnvironment(TestFileEnvironment): """ A specialized TestFileEnvironment for testing pip """ @@ -281,19 +270,11 @@ class PipTestEnvironment(scripttest.TestFileEnvironment): base_path = Path(base_path) # Store paths related to the virtual environment - _virtualenv = kwargs.pop("virtualenv") - path_locations = virtualenv.path_locations(_virtualenv) - # Make sure we have test.lib.path.Path objects - venv, lib, include, bin = map(Path, path_locations) - self.venv_path = venv - self.lib_path = virtualenv_lib_path(venv, lib) - self.include_path = include - self.bin_path = bin - - if hasattr(sys, "pypy_version_info"): - self.site_packages_path = self.venv_path.join("site-packages") - else: - self.site_packages_path = self.lib_path.join("site-packages") + venv = kwargs.pop("virtualenv") + self.venv_path = venv.location + self.lib_path = venv.lib + self.site_packages_path = venv.site + self.bin_path = venv.bin self.user_base_path = self.venv_path.join("user") self.user_site_path = self.venv_path.join( @@ -336,7 +317,7 @@ class PipTestEnvironment(scripttest.TestFileEnvironment): super(PipTestEnvironment, self).__init__(base_path, *args, **kwargs) # Expand our absolute path directories into relative - for name in ["base", "venv", "lib", "include", "bin", "site_packages", + for name in ["base", "venv", "bin", "lib", "site_packages", "user_base", "user_site", "user_bin", "scratch"]: real_name = "%s_path" % name setattr(self, name, getattr(self, real_name) - self.base_path) @@ -358,6 +339,16 @@ class PipTestEnvironment(scripttest.TestFileEnvironment): result = super(PipTestEnvironment, self)._ignore_file(fn) return result + def _find_traverse(self, path, result): + # Ignore symlinked directories to avoid duplicates in `run()` + # results because of venv `lib64 -> lib/` symlink on Linux. + full = os.path.join(self.base_path, path) + if os.path.isdir(full) and os.path.islink(full): + if not self.temp_path or path != 'tmp': + result[path] = FoundDir(self.base_path, path) + else: + super(PipTestEnvironment, self)._find_traverse(path, result) + def run(self, *args, **kw): if self.verbose: print('>> running %s %s' % (args, kw)) @@ -380,7 +371,7 @@ class PipTestEnvironment(scripttest.TestFileEnvironment): if (pyversion_tuple < (2, 7, 9) and args and args[0] in ('search', 'install', 'download')): kwargs['expect_stderr'] = True - if kwargs.pop('use_module', False): + if kwargs.pop('use_module', True): exe = 'python' args = ('-m', 'pip') + args else: @@ -394,6 +385,10 @@ class PipTestEnvironment(scripttest.TestFileEnvironment): *args, **kwargs ) + def easy_install(self, *args, **kwargs): + args = ('-m', 'easy_install') + args + return self.run('python', *args, **kwargs) + # FIXME ScriptTest does something similar, but only within a single # ProcResult; this generalizes it so states can be compared across diff --git a/tests/lib/venv.py b/tests/lib/venv.py index ce0b1059d..6b63391d9 100644 --- a/tests/lib/venv.py +++ b/tests/lib/venv.py @@ -1,12 +1,17 @@ from __future__ import absolute_import -import distutils +import compileall +import sys +import textwrap +import six import virtualenv as _virtualenv -from . import virtualenv_lib_path from .path import Path +if six.PY3: + import venv as _venv + class VirtualEnvironment(object): """ @@ -14,54 +19,165 @@ class VirtualEnvironment(object): virtualenv but in the future it could use pyvenv. """ - def __init__(self, location, system_site_packages=False): + def __init__(self, location, template=None, venv_type=None): + assert template is None or venv_type is None + assert venv_type in (None, 'virtualenv', 'venv') self.location = Path(location) - self._system_site_packages = system_site_packages + self._venv_type = venv_type or template._venv_type or 'virtualenv' + self._user_site_packages = False + self._template = template + self._sitecustomize = None + self._update_paths() + self._create() + + def _update_paths(self): home, lib, inc, bin = _virtualenv.path_locations(self.location) - self.lib = Path(virtualenv_lib_path(home, lib)) self.bin = Path(bin) + self.site = Path(lib) / 'site-packages' + # Workaround for https://github.com/pypa/virtualenv/issues/306 + if hasattr(sys, "pypy_version_info"): + version_fmt = '{0}' if six.PY3 else '{0}.{1}' + version_dir = version_fmt.format(*sys.version_info) + self.lib = Path(home, 'lib-python', version_dir) + else: + self.lib = Path(lib) def __repr__(self): return "<VirtualEnvironment {}>".format(self.location) - @classmethod - def create(cls, location, clear=False, - pip_source_dir=None, relocatable=False): - obj = cls(location) - obj._create(clear=clear, - pip_source_dir=pip_source_dir, - relocatable=relocatable) - return obj - - def _create(self, clear=False, pip_source_dir=None, relocatable=False): - # Create the actual virtual environment - _virtualenv.create_environment( - self.location, - clear=clear, - download=False, - no_pip=True, - no_wheel=True, - ) - _virtualenv.install_wheel([pip_source_dir or '.'], - self.bin.join("python")) - if relocatable: - _virtualenv.make_environment_relocatable(self.location) - # FIXME: some tests rely on 'easy-install.pth' being already present. - site_package = distutils.sysconfig.get_python_lib(prefix=self.location) - Path(site_package).join('easy-install.pth').touch() + def _create(self, clear=False): + if clear: + self.location.rmtree() + if self._template: + # On Windows, calling `_virtualenv.path_locations(target)` + # will have created the `target` directory... + if sys.platform == 'win32' and self.location.exists: + self.location.rmdir() + # Clone virtual environment from template. + self._template.location.copytree(self.location) + self._sitecustomize = self._template.sitecustomize + self._user_site_packages = self._template.user_site_packages + else: + # Create a new virtual environment. + if self._venv_type == 'virtualenv': + _virtualenv.create_environment( + self.location, + no_pip=True, + no_wheel=True, + no_setuptools=True, + ) + self._fix_virtualenv_site_module() + elif self._venv_type == 'venv': + builder = _venv.EnvBuilder() + context = builder.ensure_directories(self.location) + builder.create_configuration(context) + builder.setup_python(context) + self.site.makedirs() + self.sitecustomize = self._sitecustomize + self.user_site_packages = self._user_site_packages + + def _fix_virtualenv_site_module(self): + # Patch `site.py` so user site work as expected. + site_py = self.lib / 'site.py' + with open(site_py) as fp: + site_contents = fp.read() + for pattern, replace in ( + ( + # Ensure enabling user site does not result in adding + # the real site-packages' directory to `sys.path`. + ( + '\ndef virtual_addsitepackages(known_paths):\n' + ), + ( + '\ndef virtual_addsitepackages(known_paths):\n' + ' return known_paths\n' + ), + ), + ( + # Fix sites ordering: user site must be added before system. + ( + '\n paths_in_sys = addsitepackages(paths_in_sys)' + '\n paths_in_sys = addusersitepackages(paths_in_sys)\n' + ), + ( + '\n paths_in_sys = addusersitepackages(paths_in_sys)' + '\n paths_in_sys = addsitepackages(paths_in_sys)\n' + ), + ), + ): + assert pattern in site_contents + site_contents = site_contents.replace(pattern, replace) + with open(site_py, 'w') as fp: + fp.write(site_contents) + # Make sure bytecode is up-to-date too. + assert compileall.compile_file(str(site_py), quiet=1, force=True) + + def _customize_site(self): + contents = '' + if self._venv_type == 'venv': + # Enable user site (before system). + contents += textwrap.dedent( + ''' + import os, site, sys + + if not os.environ.get('PYTHONNOUSERSITE', False): + + site.ENABLE_USER_SITE = True + + # First, drop system-sites related paths. + original_sys_path = sys.path[:] + known_paths = set() + for path in site.getsitepackages(): + site.addsitedir(path, known_paths=known_paths) + system_paths = sys.path[len(original_sys_path):] + for path in system_paths: + if path in original_sys_path: + original_sys_path.remove(path) + sys.path = original_sys_path + + # Second, add user-site. + site.addsitedir(site.getusersitepackages()) + + # Third, add back system-sites related paths. + for path in site.getsitepackages(): + site.addsitedir(path) + ''').strip() + if self._sitecustomize is not None: + contents += '\n' + self._sitecustomize + sitecustomize = self.site / "sitecustomize.py" + sitecustomize.write(contents) + # Make sure bytecode is up-to-date too. + assert compileall.compile_file(str(sitecustomize), quiet=1, force=True) def clear(self): self._create(clear=True) + def move(self, location): + self.location.move(location) + self.location = Path(location) + self._update_paths() + @property - def system_site_packages(self): - return self._system_site_packages - - @system_site_packages.setter - def system_site_packages(self, value): - marker = self.lib.join("no-global-site-packages.txt") - if value: - marker.rm() - else: - marker.touch() - self._system_site_packages = value + def sitecustomize(self): + return self._sitecustomize + + @sitecustomize.setter + def sitecustomize(self, value): + self._sitecustomize = value + self._customize_site() + + @property + def user_site_packages(self): + return self._user_site_packages + + @user_site_packages.setter + def user_site_packages(self, value): + self._user_site_packages = value + if self._venv_type == 'virtualenv': + marker = self.lib / "no-global-site-packages.txt" + if self._user_site_packages: + marker.rm() + else: + marker.touch() + elif self._venv_type == 'venv': + self._customize_site() diff --git a/tests/unit/test_locations.py b/tests/unit/test_locations.py index 5bdfb8e6f..76dd00242 100644 --- a/tests/unit/test_locations.py +++ b/tests/unit/test_locations.py @@ -8,6 +8,7 @@ import shutil import sys import tempfile +import pytest from mock import Mock from pip._internal.locations import distutils_scheme @@ -90,6 +91,7 @@ class TestDisutilsScheme: expected = os.path.join(root, path[1:]) assert os.path.abspath(root_scheme[key]) == expected + @pytest.mark.incompatible_with_venv def test_distutils_config_file_read(self, tmpdir, monkeypatch): # This deals with nt/posix path differences install_scripts = os.path.normcase(os.path.abspath( @@ -106,6 +108,7 @@ class TestDisutilsScheme: scheme = distutils_scheme('example') assert scheme['scripts'] == install_scripts + @pytest.mark.incompatible_with_venv # when we request install-lib, we should install everything (.py & # .so) into that path; i.e. ensure platlib & purelib are set to # this path diff --git a/tools/tests-common_wheels-requirements.txt b/tools/tests-common_wheels-requirements.txt new file mode 100644 index 000000000..0a8547bcc --- /dev/null +++ b/tools/tests-common_wheels-requirements.txt @@ -0,0 +1,2 @@ +setuptools +wheel diff --git a/tools/tests-requirements.txt b/tools/tests-requirements.txt index 75e810980..f6849fc66 100644 --- a/tools/tests-requirements.txt +++ b/tools/tests-requirements.txt @@ -7,5 +7,7 @@ pytest-rerunfailures pytest-timeout pytest-xdist pyyaml +setuptools>=39.2.0 # Needed for `setuptools.wheel.Wheel` support. scripttest https://github.com/pypa/virtualenv/archive/master.zip#egg=virtualenv +wheel diff --git a/tools/travis/install.sh b/tools/travis/install.sh index ad67bb65a..3b12d69a2 100755 --- a/tools/travis/install.sh +++ b/tools/travis/install.sh @@ -3,4 +3,5 @@ set -e set -x pip install --upgrade setuptools -pip install --upgrade tox +pip install --upgrade tox tox-venv +pip freeze --all diff --git a/tools/travis/run.sh b/tools/travis/run.sh index df06b8b33..6f4c424e8 100755 --- a/tools/travis/run.sh +++ b/tools/travis/run.sh @@ -43,10 +43,10 @@ if [[ "$GROUP" == "1" ]]; then # Unit tests tox -- -m unit # Integration tests (not the ones for 'pip install') - tox -- -m integration -n 4 --duration=5 -k "not test_install" + tox -- --use-venv -m integration -n 4 --duration=5 -k "not test_install" elif [[ "$GROUP" == "2" ]]; then # Separate Job for running integration tests for 'pip install' - tox -- -m integration -n 4 --duration=5 -k "test_install" + tox -- --use-venv -m integration -n 4 --duration=5 -k "test_install" else # Non-Testing Jobs should run once tox @@ -15,6 +15,9 @@ setenv = # that our tests use. LC_CTYPE = en_US.UTF-8 deps = -r{toxinidir}/tools/tests-requirements.txt +commands_pre = + python -c 'import shutil, sys; shutil.rmtree(sys.argv[1], ignore_errors=True)' {toxinidir}/tests/data/common_wheels + {[helpers]pip} wheel -w {toxinidir}/tests/data/common_wheels -r {toxinidir}/tools/tests-common_wheels-requirements.txt commands = pytest --timeout 300 [] install_command = {[helpers]pip} install {opts} {packages} list_dependencies_command = {[helpers]pip} freeze --all @@ -41,6 +44,7 @@ skip_install = True deps = check-manifest readme_renderer +commands_pre = commands = check-manifest python setup.py check -m -r -s @@ -55,18 +59,21 @@ commands = skip_install = True basepython = python2 deps = {[lint]deps} +commands_pre = commands = {[lint]commands} [testenv:lint-py3] skip_install = True basepython = python3 deps = {[lint]deps} +commands_pre = commands = {[lint]commands} [testenv:mypy] skip_install = True basepython = python3 deps = -r{toxinidir}/tools/mypy-requirements.txt +commands_pre = commands = mypy src mypy src -2 |