Skip to content

Commit

Permalink
Add tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
jsirois committed Mar 30, 2023
1 parent c63947b commit 8117b6c
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 5 deletions.
11 changes: 7 additions & 4 deletions pex/tools/commands/venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from argparse import ArgumentParser
from collections import OrderedDict
from subprocess import CalledProcessError
from typing import Iterable, Union

from pex import pex_warnings
from pex.common import safe_delete, safe_rmtree
Expand All @@ -30,7 +29,7 @@
from pex.venv.virtualenv import PipUnavailableError, Virtualenv

if TYPE_CHECKING:
from typing import Optional
from typing import Iterable, Optional, Union

import attr # vendor:skip
else:
Expand Down Expand Up @@ -154,10 +153,14 @@ def ensure_pip_installed(
),
)
if not collisions_ok:
return Error("{message}.\nConsider re-running without --pip.".format(message=message))
return Error(
"{message}\nConsider re-running either without --pip or with --collisions-ok.".format(
message=message
)
)

pex_warnings.warn(
"{message}.\nUninstalling venv versions and using versions from the PEX.".format(
"{message}\nUninstalling venv versions and using versions from the PEX.".format(
message=message
)
)
Expand Down
2 changes: 1 addition & 1 deletion pex/venv/virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,5 +375,5 @@ def install_pip(self):
os.path.join(atomic_dir.work_dir, os.path.basename(get_pip)), "wb"
) as dst_fp:
shutil.copyfileobj(src_fp, dst_fp)
self._interpreter.execute(args=[get_pip])
self._interpreter.execute(args=[get_pip, "--no-wheel"])
return self.bin_path("pip")
294 changes: 294 additions & 0 deletions tests/integration/tools/commands/test_issue_2105.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import os.path
import subprocess
from textwrap import dedent

import pytest

from pex.dist_metadata import Distribution
from pex.pep_440 import Version
from pex.pep_503 import ProjectName
from pex.pex import PEX
from pex.testing import make_env, run_pex_command
from pex.typing import TYPE_CHECKING
from pex.venv.virtualenv import Virtualenv

if TYPE_CHECKING:
from typing import Any, Iterable, Mapping


@pytest.fixture(scope="module")
def td(tmpdir_factory):
# type: (Any) -> Any
return tmpdir_factory.mktemp("td")


PIP_PROJECT_NAME = ProjectName("pip")
SETUPTOOLS_PROJECT_NAME = ProjectName("setuptools")


def index_distributions(dists):
# type: (Iterable[Distribution]) -> Mapping[ProjectName, Version]
return {dist.metadata.project_name: dist.metadata.version for dist in dists}


@pytest.fixture(scope="module")
def baseline_venv_with_pip(td):
# type: (Any) -> Mapping[ProjectName, Version]
baseline_venv = Virtualenv.create(venv_dir=str(td.join("baseline.venv")))
baseline_venv.install_pip()
baseline_venv_distributions = index_distributions(baseline_venv.iter_distributions())
assert {PIP_PROJECT_NAME, SETUPTOOLS_PROJECT_NAME} == set(baseline_venv_distributions)
return baseline_venv_distributions


@pytest.fixture(scope="module")
def baseline_venv_pip_version(baseline_venv_with_pip):
# type: (Mapping[ProjectName, Version]) -> Version
return baseline_venv_with_pip[PIP_PROJECT_NAME]


@pytest.fixture(scope="module")
def baseline_venv_setuptools_version(baseline_venv_with_pip):
# type: (Mapping[ProjectName, Version]) -> Version
return baseline_venv_with_pip[SETUPTOOLS_PROJECT_NAME]


def assert_venv_dists(
venv_dir, # type: str
expected_pip_version, # type: Version
expected_setuptools_version, # type: Version
):
virtualenv = Virtualenv(venv_dir)
dists = index_distributions(virtualenv.iter_distributions())
assert expected_pip_version == dists[PIP_PROJECT_NAME]
assert expected_setuptools_version == dists[SETUPTOOLS_PROJECT_NAME]

def reported_version(module):
# type: (str) -> Version
return Version(
subprocess.check_output(
args=[
virtualenv.interpreter.binary,
"-c",
"import {module}; print({module}.__version__)".format(module=module),
]
).decode("utf-8")
)

assert expected_pip_version == reported_version("pip")
assert expected_setuptools_version == reported_version("setuptools")


def assert_venv_dists_no_conflicts(
tmpdir, # type: Any
pex, # type: str
expected_pip_version, # type: Version
expected_setuptools_version, # type: Version
):
# type: (...) -> None
venv_dir = os.path.join(str(tmpdir), "venv_dir")
subprocess.check_call(args=[pex, "venv", "--pip", venv_dir], env=make_env(PEX_TOOLS=1))
assert_venv_dists(venv_dir, expected_pip_version, expected_setuptools_version)


def test_pip_empty_pex(
tmpdir, # type: Any
baseline_venv_pip_version, # type: Version
baseline_venv_setuptools_version, # type: Version
):
# type: (...) -> None

pex = os.path.join(str(tmpdir), "pex")
run_pex_command(args=["-o", pex, "--include-tools"]).assert_success()

assert_venv_dists_no_conflicts(
tmpdir,
pex,
expected_pip_version=baseline_venv_pip_version,
expected_setuptools_version=baseline_venv_setuptools_version,
)


def test_pip_pex_no_conflicts(
tmpdir, # type: Any
baseline_venv_pip_version, # type: Version
baseline_venv_setuptools_version, # type: Version
):
# type: (...) -> None

pex = os.path.join(str(tmpdir), "pex")
run_pex_command(
args=[
"-o",
pex,
"pip=={version}".format(version=baseline_venv_pip_version),
"setuptools=={version}".format(version=baseline_venv_setuptools_version),
"--include-tools",
]
).assert_success()

assert_venv_dists_no_conflicts(
tmpdir,
pex,
expected_pip_version=baseline_venv_pip_version,
expected_setuptools_version=baseline_venv_setuptools_version,
)


def assert_venv_dists_conflicts(
tmpdir, # type: Any
pex, # type: str
baseline_venv_pip_version, # type: Version
baseline_venv_setuptools_version, # type: Version
expected_pip_version, # type: Version
expected_setuptools_version, # type: Version
):
# type: (...) -> None

expected_conflicts = []
if baseline_venv_pip_version != expected_pip_version:
expected_conflicts.append("pip {version}".format(version=expected_pip_version))
if baseline_venv_setuptools_version != expected_setuptools_version:
expected_conflicts.append(
"setuptools {version}".format(version=expected_setuptools_version)
)
assert (
expected_conflicts
), "The assert_venv_dists_conflicts function requires at least one conflict."

venv_dir = os.path.join(str(tmpdir), "venv_dir")
args = [pex, "venv", "--pip", venv_dir]

expected_message_prefix = (
dedent(
"""\
You asked for --pip to be installed in the venv at {venv_dir},
but the PEX at {pex} already contains:
{conflicts}
"""
)
.format(venv_dir=venv_dir, pex=pex, conflicts=os.linesep.join(expected_conflicts))
.strip()
)

process = subprocess.Popen(args, stderr=subprocess.PIPE, env=make_env(PEX_TOOLS=1))
_, stderr = process.communicate()
assert 0 != process.returncode

decoded_stderr = stderr.decode("utf-8")
assert (
dedent(
"""\
{prefix}
Consider re-running either without --pip or with --collisions-ok.
"""
).format(prefix=expected_message_prefix)
in decoded_stderr
), decoded_stderr

process = subprocess.Popen(
args + ["--force", "--collisions-ok"], stderr=subprocess.PIPE, env=make_env(PEX_TOOLS=1)
)
_, stderr = process.communicate()
assert 0 == process.returncode
decoded_stderr = stderr.decode("utf-8")
assert (
dedent(
"""\
{prefix}
Uninstalling venv versions and using versions from the PEX.
"""
).format(prefix=expected_message_prefix)
in decoded_stderr
), decoded_stderr

assert_venv_dists(venv_dir, expected_pip_version, expected_setuptools_version)


def test_pip_pex_pip_conflict(
tmpdir, # type: Any
baseline_venv_pip_version, # type: Version
baseline_venv_setuptools_version, # type: Version
):
# type: (...) -> None

pex = os.path.join(str(tmpdir), "pex")
run_pex_command(
args=[
"-o",
pex,
"pip!={version}".format(version=baseline_venv_pip_version),
"--include-tools",
]
).assert_success()
pex_pip_version = index_distributions(PEX(pex).resolve())[PIP_PROJECT_NAME]

assert_venv_dists_conflicts(
tmpdir,
pex,
baseline_venv_pip_version=baseline_venv_pip_version,
baseline_venv_setuptools_version=baseline_venv_setuptools_version,
expected_pip_version=pex_pip_version,
expected_setuptools_version=baseline_venv_setuptools_version,
)


def test_pip_pex_setuptools_conflict(
tmpdir, # type: Any
baseline_venv_pip_version, # type: Version
baseline_venv_setuptools_version, # type: Version
):
# type: (...) -> None

pex = os.path.join(str(tmpdir), "pex")
run_pex_command(
args=[
"-o",
pex,
"setuptools!={version}".format(version=baseline_venv_setuptools_version),
"--include-tools",
]
).assert_success()
pex_setuptools_version = index_distributions(PEX(pex).resolve())[SETUPTOOLS_PROJECT_NAME]

assert_venv_dists_conflicts(
tmpdir,
pex,
baseline_venv_pip_version=baseline_venv_pip_version,
baseline_venv_setuptools_version=baseline_venv_setuptools_version,
expected_pip_version=baseline_venv_pip_version,
expected_setuptools_version=pex_setuptools_version,
)


def test_pip_pex_both_conflict(
tmpdir, # type: Any
baseline_venv_pip_version, # type: Version
baseline_venv_setuptools_version, # type: Version
):
# type: (...) -> None

pex = os.path.join(str(tmpdir), "pex")
run_pex_command(
args=[
"-o",
pex,
"pip!={version}".format(version=baseline_venv_pip_version),
"setuptools!={version}".format(version=baseline_venv_setuptools_version),
"--include-tools",
]
).assert_success()
pex_pip_version = index_distributions(PEX(pex).resolve())[PIP_PROJECT_NAME]
pex_setuptools_version = index_distributions(PEX(pex).resolve())[SETUPTOOLS_PROJECT_NAME]

assert_venv_dists_conflicts(
tmpdir,
pex,
baseline_venv_pip_version=baseline_venv_pip_version,
baseline_venv_setuptools_version=baseline_venv_setuptools_version,
expected_pip_version=pex_pip_version,
expected_setuptools_version=pex_setuptools_version,
)

0 comments on commit 8117b6c

Please sign in to comment.