test_env.py (poetry-1.1.15) | : | test_env.py (poetry-1.2.0) | ||
---|---|---|---|---|
from __future__ import annotations | ||||
import os | import os | |||
import shutil | import subprocess | |||
import sys | import sys | |||
from typing import Optional | from pathlib import Path | |||
from typing import Union | from typing import TYPE_CHECKING | |||
from typing import Any | ||||
import pytest | import pytest | |||
import tomlkit | import tomlkit | |||
from clikit.io import NullIO | from cleo.io.null_io import NullIO | |||
from poetry.core.semver.version import Version | ||||
from poetry.core.semver import Version | ||||
from poetry.core.toml.file import TOMLFile | from poetry.core.toml.file import TOMLFile | |||
from poetry.factory import Factory | from poetry.factory import Factory | |||
from poetry.utils._compat import PY2 | from poetry.repositories.installed_repository import InstalledRepository | |||
from poetry.utils._compat import WINDOWS | from poetry.utils._compat import WINDOWS | |||
from poetry.utils._compat import Path | ||||
from poetry.utils.env import GET_BASE_PREFIX | from poetry.utils.env import GET_BASE_PREFIX | |||
from poetry.utils.env import EnvCommandError | from poetry.utils.env import EnvCommandError | |||
from poetry.utils.env import EnvManager | from poetry.utils.env import EnvManager | |||
from poetry.utils.env import GenericEnv | from poetry.utils.env import GenericEnv | |||
from poetry.utils.env import InvalidCurrentPythonVersionError | ||||
from poetry.utils.env import MockEnv | ||||
from poetry.utils.env import NoCompatiblePythonVersionFound | from poetry.utils.env import NoCompatiblePythonVersionFound | |||
from poetry.utils.env import SystemEnv | from poetry.utils.env import SystemEnv | |||
from poetry.utils.env import VirtualEnv | from poetry.utils.env import VirtualEnv | |||
from poetry.utils.env import build_environment | ||||
from poetry.utils.helpers import remove_directory | ||||
if TYPE_CHECKING: | ||||
from collections.abc import Callable | ||||
from collections.abc import Iterator | ||||
from pytest_mock import MockerFixture | ||||
from poetry.poetry import Poetry | ||||
from tests.conftest import Config | ||||
from tests.types import ProjectFactory | ||||
MINIMAL_SCRIPT = """\ | MINIMAL_SCRIPT = """\ | |||
print("Minimal Output"), | print("Minimal Output"), | |||
""" | """ | |||
# Script expected to fail. | # Script expected to fail. | |||
ERRORING_SCRIPT = """\ | ERRORING_SCRIPT = """\ | |||
import nullpackage | import nullpackage | |||
print("nullpackage loaded"), | print("nullpackage loaded"), | |||
""" | """ | |||
class MockVirtualEnv(VirtualEnv): | class MockVirtualEnv(VirtualEnv): | |||
def __init__(self, path, base=None, sys_path=None): | def __init__( | |||
super(MockVirtualEnv, self).__init__(path, base=base) | self, | |||
path: Path, | ||||
base: Path | None = None, | ||||
sys_path: list[str] | None = None, | ||||
) -> None: | ||||
super().__init__(path, base=base) | ||||
self._sys_path = sys_path | self._sys_path = sys_path | |||
@property | @property | |||
def sys_path(self): | def sys_path(self) -> list[str] | None: | |||
if self._sys_path is not None: | if self._sys_path is not None: | |||
return self._sys_path | return self._sys_path | |||
return super(MockVirtualEnv, self).sys_path | return super().sys_path | |||
@pytest.fixture() | @pytest.fixture() | |||
def poetry(config): | def poetry(project_factory: ProjectFactory) -> Poetry: | |||
poetry = Factory().create_poetry( | fixture = Path(__file__).parent.parent / "fixtures" / "simple_project" | |||
Path(__file__).parent.parent / "fixtures" / "simple_project" | return project_factory("simple", source=fixture) | |||
) | ||||
poetry.set_config(config) | ||||
return poetry | ||||
@pytest.fixture() | @pytest.fixture() | |||
def manager(poetry): | def manager(poetry: Poetry) -> EnvManager: | |||
return EnvManager(poetry) | return EnvManager(poetry) | |||
def test_virtualenvs_with_spaces_in_their_path_work_as_expected(tmp_dir, manager | def test_virtualenvs_with_spaces_in_their_path_work_as_expected( | |||
): | tmp_dir: str, manager: EnvManager | |||
): | ||||
venv_path = Path(tmp_dir) / "Virtual Env" | venv_path = Path(tmp_dir) / "Virtual Env" | |||
manager.build_venv(str(venv_path)) | manager.build_venv(str(venv_path)) | |||
venv = VirtualEnv(venv_path) | venv = VirtualEnv(venv_path) | |||
assert venv.run("python", "-V", shell=True).startswith("Python") | assert venv.run("python", "-V", shell=True).startswith("Python") | |||
def test_env_commands_with_spaces_in_their_arg_work_as_expected(tmp_dir, manager | @pytest.mark.skipif(sys.platform != "darwin", reason="requires darwin") | |||
): | def test_venv_backup_exclusion(tmp_dir: str, manager: EnvManager): | |||
import xattr | ||||
venv_path = Path(tmp_dir) / "Virtual Env" | ||||
manager.build_venv(str(venv_path)) | ||||
value = ( | ||||
b"bplist00_\x10\x11com.apple.backupd" | ||||
b"\x08\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00" | ||||
b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c" | ||||
) | ||||
assert ( | ||||
xattr.getxattr( | ||||
str(venv_path), "com.apple.metadata:com_apple_backup_excludeItem" | ||||
) | ||||
== value | ||||
) | ||||
def test_env_commands_with_spaces_in_their_arg_work_as_expected( | ||||
tmp_dir: str, manager: EnvManager | ||||
): | ||||
venv_path = Path(tmp_dir) / "Virtual Env" | venv_path = Path(tmp_dir) / "Virtual Env" | |||
manager.build_venv(str(venv_path)) | manager.build_venv(str(venv_path)) | |||
venv = VirtualEnv(venv_path) | venv = VirtualEnv(venv_path) | |||
assert venv.run("python", venv.pip, "--version", shell=True).startswith( | assert venv.run("python", venv.pip, "--version", shell=True).startswith( | |||
"pip {} from ".format(venv.pip_version) | f"pip {venv.pip_version} from " | |||
) | ) | |||
def test_env_shell_commands_with_stdinput_in_their_arg_work_as_expected( | def test_env_shell_commands_with_stdinput_in_their_arg_work_as_expected( | |||
tmp_dir, manager | tmp_dir: str, manager: EnvManager | |||
): | ): | |||
venv_path = Path(tmp_dir) / "Virtual Env" | venv_path = Path(tmp_dir) / "Virtual Env" | |||
manager.build_venv(str(venv_path)) | manager.build_venv(str(venv_path)) | |||
venv = VirtualEnv(venv_path) | venv = VirtualEnv(venv_path) | |||
assert venv.run("python", "-", input_=GET_BASE_PREFIX, shell=True).strip() = | run_output_path = Path( | |||
= str( | venv.run("python", "-", input_=GET_BASE_PREFIX, shell=True).strip() | |||
venv.get_base_prefix() | ||||
) | ) | |||
venv_base_prefix_path = Path(str(venv.get_base_prefix())) | ||||
assert run_output_path.resolve() == venv_base_prefix_path.resolve() | ||||
def test_env_get_supported_tags_matches_inside_virtualenv( | ||||
tmp_dir: str, manager: EnvManager | ||||
): | ||||
venv_path = Path(tmp_dir) / "Virtual Env" | ||||
manager.build_venv(str(venv_path)) | ||||
venv = VirtualEnv(venv_path) | ||||
import packaging.tags | ||||
assert venv.get_supported_tags() == list(packaging.tags.sys_tags()) | ||||
@pytest.fixture | @pytest.fixture | |||
def in_project_venv_dir(poetry): | def in_project_venv_dir(poetry: Poetry) -> Iterator[Path]: | |||
os.environ.pop("VIRTUAL_ENV", None) | os.environ.pop("VIRTUAL_ENV", None) | |||
venv_dir = poetry.file.parent.joinpath(".venv") | venv_dir = poetry.file.parent.joinpath(".venv") | |||
venv_dir.mkdir() | venv_dir.mkdir() | |||
try: | try: | |||
yield venv_dir | yield venv_dir | |||
finally: | finally: | |||
venv_dir.rmdir() | venv_dir.rmdir() | |||
@pytest.mark.parametrize("in_project", [True, False, None]) | @pytest.mark.parametrize("in_project", [True, False, None]) | |||
def test_env_get_venv_with_venv_folder_present( | def test_env_get_venv_with_venv_folder_present( | |||
manager, poetry, in_project_venv_dir, in_project | manager: EnvManager, | |||
poetry: Poetry, | ||||
in_project_venv_dir: Path, | ||||
in_project: bool | None, | ||||
): | ): | |||
poetry.config.config["virtualenvs"]["in-project"] = in_project | poetry.config.config["virtualenvs"]["in-project"] = in_project | |||
venv = manager.get() | venv = manager.get() | |||
if in_project is False: | if in_project is False: | |||
assert venv.path != in_project_venv_dir | assert venv.path != in_project_venv_dir | |||
else: | else: | |||
assert venv.path == in_project_venv_dir | assert venv.path == in_project_venv_dir | |||
def build_venv(path, executable=None): # type: (Union[Path,str], Optional[str]) -> () | def build_venv(path: Path | str, **__: Any) -> None: | |||
os.mkdir(str(path)) | os.mkdir(str(path)) | |||
def check_output_wrapper(version=Version.parse("3.7.1")): | VERSION_3_7_1 = Version.parse("3.7.1") | |||
def check_output(cmd, *args, **kwargs): | ||||
def check_output_wrapper( | ||||
version: Version = VERSION_3_7_1, | ||||
) -> Callable[[str, Any, Any], str]: | ||||
def check_output(cmd: str, *args: Any, **kwargs: Any) -> str: | ||||
if "sys.version_info[:3]" in cmd: | if "sys.version_info[:3]" in cmd: | |||
return version.text | return version.text | |||
elif "sys.version_info[:2]" in cmd: | elif "sys.version_info[:2]" in cmd: | |||
return "{}.{}".format(version.major, version.minor) | return f"{version.major}.{version.minor}" | |||
elif '-c "import sys; print(sys.executable)"' in cmd: | ||||
return f"/usr/bin/{cmd.split()[0]}" | ||||
else: | else: | |||
return str(Path("/prefix")) | return str(Path("/prefix")) | |||
return check_output | return check_output | |||
def test_activate_activates_non_existing_virtualenv_no_envs_file( | def test_activate_activates_non_existing_virtualenv_no_envs_file( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[("/prefix", None), ("/prefix", None)], | side_effect=[("/prefix", None), ("/prefix", None)], | |||
) | ) | |||
m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build _venv) | m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build _venv) | |||
env = manager.activate("python3.7", NullIO()) | env = manager.activate("python3.7", NullIO()) | |||
venv_name = EnvManager.generate_env_name("simple-project", str(poetry.file.p arent)) | venv_name = EnvManager.generate_env_name("simple-project", str(poetry.file.p arent)) | |||
m.assert_called_with( | m.assert_called_with( | |||
Path(tmp_dir) / "{}-py3.7".format(venv_name), executable="python3.7" | Path(tmp_dir) / f"{venv_name}-py3.7", | |||
executable="/usr/bin/python3.7", | ||||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="simple-project-py3.7", | ||||
) | ) | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
assert envs_file.exists() | assert envs_file.exists() | |||
envs = envs_file.read() | envs = envs_file.read() | |||
assert envs[venv_name]["minor"] == "3.7" | assert envs[venv_name]["minor"] == "3.7" | |||
assert envs[venv_name]["patch"] == "3.7.1" | assert envs[venv_name]["patch"] == "3.7.1" | |||
assert env.path == Path(tmp_dir) / "{}-py3.7".format(venv_name) | assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" | |||
assert env.base == Path("/prefix") | assert env.base == Path("/prefix") | |||
def test_activate_activates_existing_virtualenv_no_envs_file( | def test_activate_activates_existing_virtualenv_no_envs_file( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
os.mkdir(os.path.join(tmp_dir, "{}-py3.7".format(venv_name))) | os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[("/prefix", None)], | side_effect=[("/prefix", None)], | |||
) | ) | |||
m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build _venv) | m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build _venv) | |||
env = manager.activate("python3.7", NullIO()) | env = manager.activate("python3.7", NullIO()) | |||
m.assert_not_called() | m.assert_not_called() | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
assert envs_file.exists() | assert envs_file.exists() | |||
envs = envs_file.read() | envs = envs_file.read() | |||
assert envs[venv_name]["minor"] == "3.7" | assert envs[venv_name]["minor"] == "3.7" | |||
assert envs[venv_name]["patch"] == "3.7.1" | assert envs[venv_name]["patch"] == "3.7.1" | |||
assert env.path == Path(tmp_dir) / "{}-py3.7".format(venv_name) | assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" | |||
assert env.base == Path("/prefix") | assert env.base == Path("/prefix") | |||
def test_activate_activates_same_virtualenv_with_envs_file( | def test_activate_activates_same_virtualenv_with_envs_file( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
doc = tomlkit.document() | doc = tomlkit.document() | |||
doc[venv_name] = {"minor": "3.7", "patch": "3.7.1"} | doc[venv_name] = {"minor": "3.7", "patch": "3.7.1"} | |||
envs_file.write(doc) | envs_file.write(doc) | |||
os.mkdir(os.path.join(tmp_dir, "{}-py3.7".format(venv_name))) | os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[("/prefix", None)], | side_effect=[("/prefix", None)], | |||
) | ) | |||
m = mocker.patch("poetry.utils.env.EnvManager.create_venv") | m = mocker.patch("poetry.utils.env.EnvManager.create_venv") | |||
env = manager.activate("python3.7", NullIO()) | env = manager.activate("python3.7", NullIO()) | |||
m.assert_not_called() | m.assert_not_called() | |||
assert envs_file.exists() | assert envs_file.exists() | |||
envs = envs_file.read() | envs = envs_file.read() | |||
assert envs[venv_name]["minor"] == "3.7" | assert envs[venv_name]["minor"] == "3.7" | |||
assert envs[venv_name]["patch"] == "3.7.1" | assert envs[venv_name]["patch"] == "3.7.1" | |||
assert env.path == Path(tmp_dir) / "{}-py3.7".format(venv_name) | assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" | |||
assert env.base == Path("/prefix") | assert env.base == Path("/prefix") | |||
def test_activate_activates_different_virtualenv_with_envs_file( | def test_activate_activates_different_virtualenv_with_envs_file( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
doc = tomlkit.document() | doc = tomlkit.document() | |||
doc[venv_name] = {"minor": "3.7", "patch": "3.7.1"} | doc[venv_name] = {"minor": "3.7", "patch": "3.7.1"} | |||
envs_file.write(doc) | envs_file.write(doc) | |||
os.mkdir(os.path.join(tmp_dir, "{}-py3.7".format(venv_name))) | os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.6.6")), | side_effect=check_output_wrapper(Version.parse("3.6.6")), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], | side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], | |||
) | ) | |||
m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build _venv) | m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build _venv) | |||
env = manager.activate("python3.6", NullIO()) | env = manager.activate("python3.6", NullIO()) | |||
m.assert_called_with( | m.assert_called_with( | |||
Path(tmp_dir) / "{}-py3.6".format(venv_name), executable="python3.6" | Path(tmp_dir) / f"{venv_name}-py3.6", | |||
executable="/usr/bin/python3.6", | ||||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="simple-project-py3.6", | ||||
) | ) | |||
assert envs_file.exists() | assert envs_file.exists() | |||
envs = envs_file.read() | envs = envs_file.read() | |||
assert envs[venv_name]["minor"] == "3.6" | assert envs[venv_name]["minor"] == "3.6" | |||
assert envs[venv_name]["patch"] == "3.6.6" | assert envs[venv_name]["patch"] == "3.6.6" | |||
assert env.path == Path(tmp_dir) / "{}-py3.6".format(venv_name) | assert env.path == Path(tmp_dir) / f"{venv_name}-py3.6" | |||
assert env.base == Path("/prefix") | assert env.base == Path("/prefix") | |||
def test_activate_activates_recreates_for_different_patch( | def test_activate_activates_recreates_for_different_patch( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
doc = tomlkit.document() | doc = tomlkit.document() | |||
doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} | doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} | |||
envs_file.write(doc) | envs_file.write(doc) | |||
os.mkdir(os.path.join(tmp_dir, "{}-py3.7".format(venv_name))) | os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[ | side_effect=[ | |||
("/prefix", None), | ("/prefix", None), | |||
('{"version_info": [3, 7, 0]}', None), | ('{"version_info": [3, 7, 0]}', None), | |||
("/prefix", None), | ("/prefix", None), | |||
("/prefix", None), | ("/prefix", None), | |||
("/prefix", None), | ("/prefix", None), | |||
], | ], | |||
) | ) | |||
build_venv_m = mocker.patch( | build_venv_m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=build_venv | "poetry.utils.env.EnvManager.build_venv", side_effect=build_venv | |||
) | ) | |||
remove_venv_m = mocker.patch( | remove_venv_m = mocker.patch( | |||
"poetry.utils.env.EnvManager.remove_venv", side_effect=EnvManager.remove _venv | "poetry.utils.env.EnvManager.remove_venv", side_effect=EnvManager.remove _venv | |||
) | ) | |||
env = manager.activate("python3.7", NullIO()) | env = manager.activate("python3.7", NullIO()) | |||
build_venv_m.assert_called_with( | build_venv_m.assert_called_with( | |||
Path(tmp_dir) / "{}-py3.7".format(venv_name), executable="python3.7" | Path(tmp_dir) / f"{venv_name}-py3.7", | |||
executable="/usr/bin/python3.7", | ||||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="simple-project-py3.7", | ||||
) | ) | |||
remove_venv_m.assert_called_with(Path(tmp_dir) / "{}-py3.7".format(venv_name )) | remove_venv_m.assert_called_with(Path(tmp_dir) / f"{venv_name}-py3.7") | |||
assert envs_file.exists() | assert envs_file.exists() | |||
envs = envs_file.read() | envs = envs_file.read() | |||
assert envs[venv_name]["minor"] == "3.7" | assert envs[venv_name]["minor"] == "3.7" | |||
assert envs[venv_name]["patch"] == "3.7.1" | assert envs[venv_name]["patch"] == "3.7.1" | |||
assert env.path == Path(tmp_dir) / "{}-py3.7".format(venv_name) | assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" | |||
assert env.base == Path("/prefix") | assert env.base == Path("/prefix") | |||
assert (Path(tmp_dir) / "{}-py3.7".format(venv_name)).exists() | assert (Path(tmp_dir) / f"{venv_name}-py3.7").exists() | |||
def test_activate_does_not_recreate_when_switching_minor( | def test_activate_does_not_recreate_when_switching_minor( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
doc = tomlkit.document() | doc = tomlkit.document() | |||
doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} | doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} | |||
envs_file.write(doc) | envs_file.write(doc) | |||
os.mkdir(os.path.join(tmp_dir, "{}-py3.7".format(venv_name))) | os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.7")) | |||
os.mkdir(os.path.join(tmp_dir, "{}-py3.6".format(venv_name))) | os.mkdir(os.path.join(tmp_dir, f"{venv_name}-py3.6")) | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.6.6")), | side_effect=check_output_wrapper(Version.parse("3.6.6")), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], | side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], | |||
) | ) | |||
build_venv_m = mocker.patch( | build_venv_m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=build_venv | "poetry.utils.env.EnvManager.build_venv", side_effect=build_venv | |||
) | ) | |||
remove_venv_m = mocker.patch( | remove_venv_m = mocker.patch( | |||
"poetry.utils.env.EnvManager.remove_venv", side_effect=EnvManager.remove _venv | "poetry.utils.env.EnvManager.remove_venv", side_effect=EnvManager.remove _venv | |||
) | ) | |||
env = manager.activate("python3.6", NullIO()) | env = manager.activate("python3.6", NullIO()) | |||
build_venv_m.assert_not_called() | build_venv_m.assert_not_called() | |||
remove_venv_m.assert_not_called() | remove_venv_m.assert_not_called() | |||
assert envs_file.exists() | assert envs_file.exists() | |||
envs = envs_file.read() | envs = envs_file.read() | |||
assert envs[venv_name]["minor"] == "3.6" | assert envs[venv_name]["minor"] == "3.6" | |||
assert envs[venv_name]["patch"] == "3.6.6" | assert envs[venv_name]["patch"] == "3.6.6" | |||
assert env.path == Path(tmp_dir) / "{}-py3.6".format(venv_name) | assert env.path == Path(tmp_dir) / f"{venv_name}-py3.6" | |||
assert env.base == Path("/prefix") | assert env.base == Path("/prefix") | |||
assert (Path(tmp_dir) / "{}-py3.6".format(venv_name)).exists() | assert (Path(tmp_dir) / f"{venv_name}-py3.6").exists() | |||
def test_deactivate_non_activated_but_existing( | def test_deactivate_non_activated_but_existing( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
( | python = ".".join(str(c) for c in sys.version_info[:2]) | |||
Path(tmp_dir) | (Path(tmp_dir) / f"{venv_name}-py{python}").mkdir() | |||
/ "{}-py{}".format(venv_name, ".".join(str(c) for c in sys.version_info[ | ||||
:2])) | ||||
).mkdir() | ||||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
manager.deactivate(NullIO()) | manager.deactivate(NullIO()) | |||
env = manager.get() | env = manager.get() | |||
assert env.path == Path(tmp_dir) / "{}-py{}".format( | assert env.path == Path(tmp_dir) / f"{venv_name}-py{python}" | |||
venv_name, ".".join(str(c) for c in sys.version_info[:2]) | ||||
) | ||||
assert Path("/prefix") | assert Path("/prefix") | |||
def test_deactivate_activated(tmp_dir, manager, poetry, config, mocker): | def test_deactivate_activated( | |||
tmp_dir: str, | ||||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ||||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
version = Version.parse(".".join(str(c) for c in sys.version_info[:3])) | version = Version.from_parts(*sys.version_info[:3]) | |||
other_version = Version.parse("3.4") if version.major == 2 else version.next | other_version = Version.parse("3.4") if version.major == 2 else version.next | |||
_minor | _minor() | |||
( | (Path(tmp_dir) / f"{venv_name}-py{version.major}.{version.minor}").mkdir() | |||
Path(tmp_dir) / "{}-py{}.{}".format(venv_name, version.major, version.mi | ||||
nor) | ||||
).mkdir() | ||||
( | ( | |||
Path(tmp_dir) | Path(tmp_dir) / f"{venv_name}-py{other_version.major}.{other_version.min | |||
/ "{}-py{}.{}".format(venv_name, other_version.major, other_version.mino | or}" | |||
r) | ||||
).mkdir() | ).mkdir() | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
doc = tomlkit.document() | doc = tomlkit.document() | |||
doc[venv_name] = { | doc[venv_name] = { | |||
"minor": "{}.{}".format(other_version.major, other_version.minor), | "minor": f"{other_version.major}.{other_version.minor}", | |||
"patch": other_version.text, | "patch": other_version.text, | |||
} | } | |||
envs_file.write(doc) | envs_file.write(doc) | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
manager.deactivate(NullIO()) | manager.deactivate(NullIO()) | |||
env = manager.get() | env = manager.get() | |||
assert env.path == Path(tmp_dir) / "{}-py{}.{}".format( | assert env.path == Path(tmp_dir) / f"{venv_name}-py{version.major}.{version. | |||
venv_name, version.major, version.minor | minor}" | |||
) | ||||
assert Path("/prefix") | assert Path("/prefix") | |||
envs = envs_file.read() | envs = envs_file.read() | |||
assert len(envs) == 0 | assert len(envs) == 0 | |||
def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( | def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( | |||
tmp_dir, manager, poetry, config, mocker | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
os.environ["VIRTUAL_ENV"] = "/environment/prefix" | os.environ["VIRTUAL_ENV"] = "/environment/prefix" | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
(Path(tmp_dir) / "{}-py3.7".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
doc = tomlkit.document() | doc = tomlkit.document() | |||
doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} | doc[venv_name] = {"minor": "3.7", "patch": "3.7.0"} | |||
envs_file.write(doc) | envs_file.write(doc) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[("/prefix", None)], | side_effect=[("/prefix", None)], | |||
) | ) | |||
env = manager.get() | env = manager.get() | |||
assert env.path == Path(tmp_dir) / "{}-py3.7".format(venv_name) | assert env.path == Path(tmp_dir) / f"{venv_name}-py3.7" | |||
assert env.base == Path("/prefix") | assert env.base == Path("/prefix") | |||
def test_list(tmp_dir, manager, poetry, config): | def test_list(tmp_dir: str, manager: EnvManager, poetry: Poetry, config: Config) : | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
(Path(tmp_dir) / "{}-py3.7".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() | |||
(Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() | |||
venvs = manager.list() | venvs = manager.list() | |||
assert 2 == len(venvs) | assert len(venvs) == 2 | |||
assert (Path(tmp_dir) / "{}-py3.6".format(venv_name)) == venvs[0].path | assert venvs[0].path == (Path(tmp_dir) / f"{venv_name}-py3.6") | |||
assert (Path(tmp_dir) / "{}-py3.7".format(venv_name)) == venvs[1].path | assert venvs[1].path == (Path(tmp_dir) / f"{venv_name}-py3.7") | |||
def test_remove_by_python_version(tmp_dir, manager, poetry, config, mocker): | def test_remove_by_python_version( | |||
tmp_dir: str, | ||||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ||||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
(Path(tmp_dir) / "{}-py3.7".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() | |||
(Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.6.6")), | side_effect=check_output_wrapper(Version.parse("3.6.6")), | |||
) | ) | |||
venv = manager.remove("3.6") | venv = manager.remove("3.6") | |||
assert (Path(tmp_dir) / "{}-py3.6".format(venv_name)) == venv.path | expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" | |||
assert not (Path(tmp_dir) / "{}-py3.6".format(venv_name)).exists() | assert venv.path == expected_venv_path | |||
assert not expected_venv_path.exists() | ||||
def test_remove_by_name(tmp_dir, manager, poetry, config, mocker): | ||||
def test_remove_by_name( | ||||
tmp_dir: str, | ||||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ||||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
(Path(tmp_dir) / "{}-py3.7".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() | |||
(Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.6.6")), | side_effect=check_output_wrapper(Version.parse("3.6.6")), | |||
) | ) | |||
venv = manager.remove("{}-py3.6".format(venv_name)) | venv = manager.remove(f"{venv_name}-py3.6") | |||
assert (Path(tmp_dir) / "{}-py3.6".format(venv_name)) == venv.path | expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" | |||
assert not (Path(tmp_dir) / "{}-py3.6".format(venv_name)).exists() | assert venv.path == expected_venv_path | |||
assert not expected_venv_path.exists() | ||||
def test_remove_also_deactivates(tmp_dir, manager, poetry, config, mocker): | ||||
def test_remove_also_deactivates( | ||||
tmp_dir: str, | ||||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ||||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
(Path(tmp_dir) / "{}-py3.7".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.7").mkdir() | |||
(Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir() | (Path(tmp_dir) / f"{venv_name}-py3.6").mkdir() | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.6.6")), | side_effect=check_output_wrapper(Version.parse("3.6.6")), | |||
) | ) | |||
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") | |||
doc = tomlkit.document() | doc = tomlkit.document() | |||
doc[venv_name] = {"minor": "3.6", "patch": "3.6.6"} | doc[venv_name] = {"minor": "3.6", "patch": "3.6.6"} | |||
envs_file.write(doc) | envs_file.write(doc) | |||
venv = manager.remove("python3.6") | venv = manager.remove("python3.6") | |||
assert (Path(tmp_dir) / "{}-py3.6".format(venv_name)) == venv.path | expected_venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" | |||
assert not (Path(tmp_dir) / "{}-py3.6".format(venv_name)).exists() | assert venv.path == expected_venv_path | |||
assert not expected_venv_path.exists() | ||||
envs = envs_file.read() | envs = envs_file.read() | |||
assert venv_name not in envs | assert venv_name not in envs | |||
def test_remove_keeps_dir_if_not_deleteable(tmp_dir, manager, poetry, config, mo | def test_remove_keeps_dir_if_not_deleteable( | |||
cker): | tmp_dir: str, | |||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
): | ||||
# Ensure we empty rather than delete folder if its is an active mount point. | # Ensure we empty rather than delete folder if its is an active mount point. | |||
# See https://github.com/python-poetry/poetry/pull/2064 | # See https://github.com/python-poetry/poetry/pull/2064 | |||
config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | config.merge({"virtualenvs": {"path": str(tmp_dir)}}) | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
venv_path = Path(tmp_dir) / "{}-py3.6".format(venv_name) | venv_path = Path(tmp_dir) / f"{venv_name}-py3.6" | |||
venv_path.mkdir() | venv_path.mkdir() | |||
folder1_path = venv_path / "folder1" | folder1_path = venv_path / "folder1" | |||
folder1_path.mkdir() | folder1_path.mkdir() | |||
file1_path = folder1_path / "file1" | file1_path = folder1_path / "file1" | |||
file1_path.touch(exist_ok=False) | file1_path.touch(exist_ok=False) | |||
file2_path = venv_path / "file2" | file2_path = venv_path / "file2" | |||
file2_path.touch(exist_ok=False) | file2_path.touch(exist_ok=False) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.6.6")), | side_effect=check_output_wrapper(Version.parse("3.6.6")), | |||
) | ) | |||
original_rmtree = shutil.rmtree | def err_on_rm_venv_only(path: Path | str, *args: Any, **kwargs: Any) -> None | |||
: | ||||
def err_on_rm_venv_only(path, *args, **kwargs): | if str(path) == str(venv_path): | |||
if path == str(venv_path): | ||||
raise OSError(16, "Test error") # ERRNO 16: Device or resource busy | raise OSError(16, "Test error") # ERRNO 16: Device or resource busy | |||
else: | else: | |||
original_rmtree(path) | remove_directory(path) | |||
m = mocker.patch("shutil.rmtree", side_effect=err_on_rm_venv_only) | m = mocker.patch( | |||
"poetry.utils.env.remove_directory", side_effect=err_on_rm_venv_only | ||||
) | ||||
venv = manager.remove("{}-py3.6".format(venv_name)) | venv = manager.remove(f"{venv_name}-py3.6") | |||
m.assert_any_call(str(venv_path)) | m.assert_any_call(venv_path) | |||
assert venv_path == venv.path | assert venv_path == venv.path | |||
assert venv_path.exists() | assert venv_path.exists() | |||
assert not folder1_path.exists() | assert not folder1_path.exists() | |||
assert not file1_path.exists() | assert not file1_path.exists() | |||
assert not file2_path.exists() | assert not file2_path.exists() | |||
m.side_effect = original_rmtree # Avoid teardown using `err_on_rm_venv_only ` | m.side_effect = remove_directory # Avoid teardown using `err_on_rm_venv_onl y` | |||
@pytest.mark.skipif( | @pytest.mark.skipif(os.name == "nt", reason="Symlinks are not support for Window | |||
os.name == "nt" or PY2, reason="Symlinks are not support for Windows" | s") | |||
) | def test_env_has_symlinks_on_nix(tmp_dir: str, tmp_venv: VirtualEnv): | |||
def test_env_has_symlinks_on_nix(tmp_dir, tmp_venv): | ||||
assert os.path.islink(tmp_venv.python) | assert os.path.islink(tmp_venv.python) | |||
def test_run_with_input(tmp_dir, tmp_venv): | def test_run_with_input(tmp_dir: str, tmp_venv: VirtualEnv): | |||
result = tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT) | result = tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT) | |||
assert result == "Minimal Output" + os.linesep | assert result == "Minimal Output" + os.linesep | |||
def test_run_with_input_non_zero_return(tmp_dir, tmp_venv): | def test_run_with_input_non_zero_return(tmp_dir: str, tmp_venv: VirtualEnv): | |||
with pytest.raises(EnvCommandError) as process_error: | ||||
with pytest.raises(EnvCommandError) as processError: | ||||
# Test command that will return non-zero returncode. | # Test command that will return non-zero returncode. | |||
tmp_venv.run("python", "-", input_=ERRORING_SCRIPT) | tmp_venv.run("python", "-", input_=ERRORING_SCRIPT) | |||
assert processError.value.e.returncode == 1 | assert process_error.value.e.returncode == 1 | |||
def test_run_with_keyboard_interrupt( | ||||
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture | ||||
): | ||||
mocker.patch("subprocess.run", side_effect=KeyboardInterrupt()) | ||||
with pytest.raises(KeyboardInterrupt): | ||||
tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT) | ||||
subprocess.run.assert_called_once() | ||||
def test_call_with_input_and_keyboard_interrupt( | ||||
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture | ||||
): | ||||
mocker.patch("subprocess.run", side_effect=KeyboardInterrupt()) | ||||
kwargs = {"call": True} | ||||
with pytest.raises(KeyboardInterrupt): | ||||
tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT, **kwargs) | ||||
subprocess.run.assert_called_once() | ||||
def test_call_no_input_with_keyboard_interrupt( | ||||
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture | ||||
): | ||||
mocker.patch("subprocess.call", side_effect=KeyboardInterrupt()) | ||||
kwargs = {"call": True} | ||||
with pytest.raises(KeyboardInterrupt): | ||||
tmp_venv.run("python", "-", **kwargs) | ||||
subprocess.call.assert_called_once() | ||||
def test_run_with_called_process_error( | ||||
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture | ||||
): | ||||
mocker.patch( | ||||
"subprocess.run", side_effect=subprocess.CalledProcessError(42, "some_co | ||||
mmand") | ||||
) | ||||
with pytest.raises(EnvCommandError): | ||||
tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT) | ||||
subprocess.run.assert_called_once() | ||||
def test_call_with_input_and_called_process_error( | ||||
tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture | ||||
): | ||||
mocker.patch( | ||||
"subprocess.run", side_effect=subprocess.CalledProcessError(42, "some_co | ||||
mmand") | ||||
) | ||||
kwargs = {"call": True} | ||||
with pytest.raises(EnvCommandError): | ||||
tmp_venv.run("python", "-", input_=MINIMAL_SCRIPT, **kwargs) | ||||
subprocess.run.assert_called_once() | ||||
def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ | def test_call_no_input_with_called_process_error( | |||
ones_first( | tmp_dir: str, tmp_venv: VirtualEnv, mocker: MockerFixture | |||
manager, poetry, config, mocker | ): | |||
mocker.patch( | ||||
"subprocess.call", side_effect=subprocess.CalledProcessError(42, "some_c | ||||
ommand") | ||||
) | ||||
kwargs = {"call": True} | ||||
with pytest.raises(EnvCommandError): | ||||
tmp_venv.run("python", "-", **kwargs) | ||||
subprocess.call.assert_called_once() | ||||
def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ | ||||
ones_first( # noqa: E501 | ||||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
config_virtualenvs_path: Path, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
poetry.package.python_versions = "^3.6" | poetry.package.python_versions = "^3.6" | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
mocker.patch("sys.version_info", (2, 7, 16)) | mocker.patch("sys.version_info", (2, 7, 16)) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.7.5")), | side_effect=check_output_wrapper(Version.parse("3.7.5")), | |||
) | ) | |||
m = mocker.patch( | m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | |||
) | ) | |||
manager.create_venv(NullIO()) | manager.create_venv(NullIO()) | |||
m.assert_called_with( | m.assert_called_with( | |||
Path("/foo/virtualenvs/{}-py3.7".format(venv_name)), executable="python3 | config_virtualenvs_path / f"{venv_name}-py3.7", | |||
" | executable="python3", | |||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="simple-project-py3.7", | ||||
) | ) | |||
def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific _ones( | def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific _ones( | |||
manager, poetry, config, mocker | manager: EnvManager, | |||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
config_virtualenvs_path: Path, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
poetry.package.python_versions = "^3.6" | poetry.package.python_versions = "^3.6" | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
mocker.patch("sys.version_info", (2, 7, 16)) | mocker.patch("sys.version_info", (2, 7, 16)) | |||
mocker.patch( | mocker.patch("subprocess.check_output", side_effect=["3.5.3", "3.9.0"]) | |||
"poetry.utils._compat.subprocess.check_output", side_effect=["3.5.3", "3 | ||||
.9.0"] | ||||
) | ||||
m = mocker.patch( | m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | |||
) | ) | |||
manager.create_venv(NullIO()) | manager.create_venv(NullIO()) | |||
m.assert_called_with( | m.assert_called_with( | |||
Path("/foo/virtualenvs/{}-py3.9".format(venv_name)), executable="python3 | config_virtualenvs_path / f"{venv_name}-py3.9", | |||
.9" | executable="python3.9", | |||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="simple-project-py3.9", | ||||
) | ) | |||
def test_create_venv_fails_if_no_compatible_python_version_could_be_found( | def test_create_venv_fails_if_no_compatible_python_version_could_be_found( | |||
manager, poetry, config, mocker | manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture | |||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
poetry.package.python_versions = "^4.8" | poetry.package.python_versions = "^4.8" | |||
mocker.patch( | mocker.patch("subprocess.check_output", side_effect=["", "", "", ""]) | |||
"poetry.utils._compat.subprocess.check_output", side_effect=["", "", "", | ||||
""] | ||||
) | ||||
m = mocker.patch( | m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | |||
) | ) | |||
with pytest.raises(NoCompatiblePythonVersionFound) as e: | with pytest.raises(NoCompatiblePythonVersionFound) as e: | |||
manager.create_venv(NullIO()) | manager.create_venv(NullIO()) | |||
expected_message = ( | expected_message = ( | |||
"Poetry was unable to find a compatible version. " | "Poetry was unable to find a compatible version. " | |||
"If you have one, you can explicitly use it " | "If you have one, you can explicitly use it " | |||
'via the "env use" command.' | 'via the "env use" command.' | |||
) | ) | |||
assert expected_message == str(e.value) | assert str(e.value) == expected_message | |||
assert 0 == m.call_count | assert m.call_count == 0 | |||
def test_create_venv_does_not_try_to_find_compatible_versions_with_executable( | def test_create_venv_does_not_try_to_find_compatible_versions_with_executable( | |||
manager, poetry, config, mocker | manager: EnvManager, poetry: Poetry, config: Config, mocker: MockerFixture | |||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
poetry.package.python_versions = "^4.8" | poetry.package.python_versions = "^4.8" | |||
mocker.patch("poetry.utils._compat.subprocess.check_output", side_effect=["3 .8.0"]) | mocker.patch("subprocess.check_output", side_effect=["3.8.0"]) | |||
m = mocker.patch( | m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | |||
) | ) | |||
with pytest.raises(NoCompatiblePythonVersionFound) as e: | with pytest.raises(NoCompatiblePythonVersionFound) as e: | |||
manager.create_venv(NullIO(), executable="3.8") | manager.create_venv(NullIO(), executable="3.8") | |||
expected_message = ( | expected_message = ( | |||
"The specified Python version (3.8.0) is not supported by the project (^ 4.8).\n" | "The specified Python version (3.8.0) is not supported by the project (^ 4.8).\n" | |||
"Please choose a compatible version or loosen the python constraint " | "Please choose a compatible version or loosen the python constraint " | |||
"specified in the pyproject.toml file." | "specified in the pyproject.toml file." | |||
) | ) | |||
assert expected_message == str(e.value) | assert str(e.value) == expected_message | |||
assert 0 == m.call_count | assert m.call_count == 0 | |||
def test_create_venv_uses_patch_version_to_detect_compatibility( | def test_create_venv_uses_patch_version_to_detect_compatibility( | |||
manager, poetry, config, mocker | manager: EnvManager, | |||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
config_virtualenvs_path: Path, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
version = Version.parse(".".join(str(c) for c in sys.version_info[:3])) | version = Version.from_parts(*sys.version_info[:3]) | |||
poetry.package.python_versions = "^{}".format( | poetry.package.python_versions = "^" + ".".join( | |||
".".join(str(c) for c in sys.version_info[:3]) | str(c) for c in sys.version_info[:3] | |||
) | ) | |||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
mocker.patch("sys.version_info", (version.major, version.minor, version.patc h + 1)) | mocker.patch("sys.version_info", (version.major, version.minor, version.patc h + 1)) | |||
check_output = mocker.patch( | check_output = mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(Version.parse("3.6.9")), | side_effect=check_output_wrapper(Version.parse("3.6.9")), | |||
) | ) | |||
m = mocker.patch( | m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | |||
) | ) | |||
manager.create_venv(NullIO()) | manager.create_venv(NullIO()) | |||
assert not check_output.called | assert not check_output.called | |||
m.assert_called_with( | m.assert_called_with( | |||
Path( | config_virtualenvs_path / f"{venv_name}-py{version.major}.{version.minor | |||
"/foo/virtualenvs/{}-py{}.{}".format( | }", | |||
venv_name, version.major, version.minor | ||||
) | ||||
), | ||||
executable=None, | executable=None, | |||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt=f"simple-project-py{version.major}.{version.minor}", | ||||
) | ) | |||
def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( | def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( | |||
manager, poetry, config, mocker | manager: EnvManager, | |||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
config_virtualenvs_path: Path, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
version = Version.parse(".".join(str(c) for c in sys.version_info[:3])) | version = Version.from_parts(*sys.version_info[:3]) | |||
poetry.package.python_versions = "~{}".format( | poetry.package.python_versions = f"~{version.major}.{version.minor-1}.0" | |||
".".join(str(c) for c in (version.major, version.minor - 1, 0)) | ||||
) | ||||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare nt)) | |||
check_output = mocker.patch( | check_output = mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper( | side_effect=check_output_wrapper( | |||
Version.parse("{}.{}.0".format(version.major, version.minor - 1)) | Version.parse(f"{version.major}.{version.minor - 1}.0") | |||
), | ), | |||
) | ) | |||
m = mocker.patch( | m = mocker.patch( | |||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw args: "" | |||
) | ) | |||
manager.create_venv( | manager.create_venv( | |||
NullIO(), executable="python{}.{}".format(version.major, version.minor - 1) | NullIO(), executable=f"python{version.major}.{version.minor - 1}" | |||
) | ) | |||
assert check_output.called | assert check_output.called | |||
m.assert_called_with( | m.assert_called_with( | |||
Path( | config_virtualenvs_path / f"{venv_name}-py{version.major}.{version.minor | |||
"/foo/virtualenvs/{}-py{}.{}".format( | - 1}", | |||
venv_name, version.major, version.minor - 1 | executable=f"python{version.major}.{version.minor - 1}", | |||
) | flags={ | |||
), | "always-copy": False, | |||
executable="python{}.{}".format(version.major, version.minor - 1), | "system-site-packages": False, | |||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt=f"simple-project-py{version.major}.{version.minor - 1}", | ||||
) | ||||
def test_create_venv_fails_if_current_python_version_is_not_supported( | ||||
manager: EnvManager, poetry: Poetry | ||||
): | ||||
if "VIRTUAL_ENV" in os.environ: | ||||
del os.environ["VIRTUAL_ENV"] | ||||
manager.create_venv(NullIO()) | ||||
current_version = Version.parse(".".join(str(c) for c in sys.version_info[:3 | ||||
])) | ||||
next_version = ".".join( | ||||
str(c) for c in (current_version.major, current_version.minor + 1, 0) | ||||
) | ||||
package_version = "~" + next_version | ||||
poetry.package.python_versions = package_version | ||||
with pytest.raises(InvalidCurrentPythonVersionError) as e: | ||||
manager.create_venv(NullIO()) | ||||
expected_message = ( | ||||
f"Current Python version ({current_version}) is not allowed by the proje | ||||
ct" | ||||
f' ({package_version}).\nPlease change python executable via the "env us | ||||
e"' | ||||
" command." | ||||
) | ) | |||
assert expected_message == str(e.value) | ||||
def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( | def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( | |||
manager, poetry, config, tmp_dir, mocker | manager: EnvManager, | |||
poetry: Poetry, | ||||
config: Config, | ||||
tmp_dir: str, | ||||
mocker: MockerFixture, | ||||
): | ): | |||
if "VIRTUAL_ENV" in os.environ: | if "VIRTUAL_ENV" in os.environ: | |||
del os.environ["VIRTUAL_ENV"] | del os.environ["VIRTUAL_ENV"] | |||
config.merge( | config.merge( | |||
{ | { | |||
"virtualenvs": { | "virtualenvs": { | |||
"path": str(Path(tmp_dir) / "virtualenvs"), | "path": str(Path(tmp_dir) / "virtualenvs"), | |||
"in-project": True, | "in-project": True, | |||
} | } | |||
} | } | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.check_output", | "subprocess.check_output", | |||
side_effect=check_output_wrapper(), | side_effect=check_output_wrapper(), | |||
) | ) | |||
mocker.patch( | mocker.patch( | |||
"poetry.utils._compat.subprocess.Popen.communicate", | "subprocess.Popen.communicate", | |||
side_effect=[("/prefix", None), ("/prefix", None)], | side_effect=[("/prefix", None), ("/prefix", None)], | |||
) | ) | |||
m = mocker.patch("poetry.utils.env.EnvManager.build_venv") | m = mocker.patch("poetry.utils.env.EnvManager.build_venv") | |||
manager.activate("python3.7", NullIO()) | manager.activate("python3.7", NullIO()) | |||
m.assert_called_with(poetry.file.parent / ".venv", executable="python3.7") | m.assert_called_with( | |||
poetry.file.parent / ".venv", | ||||
executable="/usr/bin/python3.7", | ||||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="simple-project-py3.7", | ||||
) | ||||
envs_file = TOMLFile(Path(tmp_dir) / "virtualenvs" / "envs.toml") | envs_file = TOMLFile(Path(tmp_dir) / "virtualenvs" / "envs.toml") | |||
assert not envs_file.exists() | assert not envs_file.exists() | |||
def test_system_env_has_correct_paths(): | def test_system_env_has_correct_paths(): | |||
env = SystemEnv(Path(sys.prefix)) | env = SystemEnv(Path(sys.prefix)) | |||
paths = env.paths | paths = env.paths | |||
assert paths.get("purelib") is not None | assert paths.get("purelib") is not None | |||
assert paths.get("platlib") is not None | assert paths.get("platlib") is not None | |||
assert paths.get("scripts") is not None | assert paths.get("scripts") is not None | |||
assert env.site_packages.path == Path(paths["purelib"]) | assert env.site_packages.path == Path(paths["purelib"]) | |||
@pytest.mark.parametrize( | @pytest.mark.parametrize( | |||
("enabled",), [(True,), (False,)], | "enabled", | |||
[True, False], | ||||
) | ) | |||
def test_system_env_usersite(mocker, enabled): | def test_system_env_usersite(mocker: MockerFixture, enabled: bool): | |||
mocker.patch("site.check_enableusersite", return_value=enabled) | mocker.patch("site.check_enableusersite", return_value=enabled) | |||
env = SystemEnv(Path(sys.prefix)) | env = SystemEnv(Path(sys.prefix)) | |||
assert (enabled and env.usersite is not None) or ( | assert (enabled and env.usersite is not None) or ( | |||
not enabled and env.usersite is None | not enabled and env.usersite is None | |||
) | ) | |||
def test_venv_has_correct_paths(tmp_venv): | def test_venv_has_correct_paths(tmp_venv: VirtualEnv): | |||
paths = tmp_venv.paths | paths = tmp_venv.paths | |||
assert paths.get("purelib") is not None | assert paths.get("purelib") is not None | |||
assert paths.get("platlib") is not None | assert paths.get("platlib") is not None | |||
assert paths.get("scripts") is not None | assert paths.get("scripts") is not None | |||
assert tmp_venv.site_packages.path == Path(paths["purelib"]) | assert tmp_venv.site_packages.path == Path(paths["purelib"]) | |||
def test_env_finds_the_correct_executables(tmp_dir, manager): | def test_env_system_packages(tmp_path: Path, poetry: Poetry): | |||
venv_path = tmp_path / "venv" | ||||
pyvenv_cfg = venv_path / "pyvenv.cfg" | ||||
EnvManager(poetry).build_venv(path=venv_path, flags={"system-site-packages": | ||||
True}) | ||||
assert "include-system-site-packages = true" in pyvenv_cfg.read_text() | ||||
@pytest.mark.parametrize( | ||||
("flags", "packages"), | ||||
[ | ||||
({"no-pip": False}, {"pip", "wheel"}), | ||||
({"no-pip": False, "no-wheel": True}, {"pip"}), | ||||
({"no-pip": True}, set()), | ||||
({"no-setuptools": False}, {"setuptools"}), | ||||
({"no-setuptools": True}, set()), | ||||
({"no-pip": True, "no-setuptools": False}, {"setuptools"}), | ||||
({"no-wheel": False}, {"wheel"}), | ||||
({}, set()), | ||||
], | ||||
) | ||||
def test_env_no_pip( | ||||
tmp_path: Path, poetry: Poetry, flags: dict[str, bool], packages: set[str] | ||||
): | ||||
venv_path = tmp_path / "venv" | ||||
EnvManager(poetry).build_venv(path=venv_path, flags=flags) | ||||
env = VirtualEnv(venv_path) | ||||
installed_repository = InstalledRepository.load(env=env, with_dependencies=T | ||||
rue) | ||||
installed_packages = { | ||||
package.name | ||||
for package in installed_repository.packages | ||||
# workaround for BSD test environments | ||||
if package.name != "sqlite3" | ||||
} | ||||
assert installed_packages == packages | ||||
def test_env_finds_the_correct_executables(tmp_dir: str, manager: EnvManager): | ||||
venv_path = Path(tmp_dir) / "Virtual Env" | venv_path = Path(tmp_dir) / "Virtual Env" | |||
manager.build_venv(str(venv_path)) | manager.build_venv(str(venv_path), with_pip=True) | |||
venv = VirtualEnv(venv_path) | venv = VirtualEnv(venv_path) | |||
default_executable = expected_executable = "python" + (".exe" if WINDOWS els | default_executable = expected_executable = f"python{'.exe' if WINDOWS else ' | |||
e "") | '}" | |||
default_pip_executable = expected_pip_executable = "pip" + ( | default_pip_executable = expected_pip_executable = f"pip{'.exe' if WINDOWS e | |||
".exe" if WINDOWS else "" | lse ''}" | |||
) | major_executable = f"python{sys.version_info[0]}{'.exe' if WINDOWS else ''}" | |||
major_executable = "python{}{}".format( | major_pip_executable = f"pip{sys.version_info[0]}{'.exe' if WINDOWS else ''} | |||
sys.version_info[0], ".exe" if WINDOWS else "" | " | |||
) | ||||
major_pip_executable = "pip{}{}".format( | ||||
sys.version_info[0], ".exe" if WINDOWS else "" | ||||
) | ||||
if ( | if ( | |||
venv._bin_dir.joinpath(default_executable).exists() | venv._bin_dir.joinpath(default_executable).exists() | |||
and venv._bin_dir.joinpath(major_executable).exists() | and venv._bin_dir.joinpath(major_executable).exists() | |||
): | ): | |||
venv._bin_dir.joinpath(default_executable).unlink() | venv._bin_dir.joinpath(default_executable).unlink() | |||
expected_executable = major_executable | expected_executable = major_executable | |||
if ( | if ( | |||
venv._bin_dir.joinpath(default_pip_executable).exists() | venv._bin_dir.joinpath(default_pip_executable).exists() | |||
and venv._bin_dir.joinpath(major_pip_executable).exists() | and venv._bin_dir.joinpath(major_pip_executable).exists() | |||
): | ): | |||
venv._bin_dir.joinpath(default_pip_executable).unlink() | venv._bin_dir.joinpath(default_pip_executable).unlink() | |||
expected_pip_executable = major_pip_executable | expected_pip_executable = major_pip_executable | |||
venv = VirtualEnv(venv_path) | venv = VirtualEnv(venv_path) | |||
assert Path(venv.python).name == expected_executable | assert Path(venv.python).name == expected_executable | |||
assert Path(venv.pip).name.startswith(expected_pip_executable.split(".")[0]) | assert Path(venv.pip).name.startswith(expected_pip_executable.split(".")[0]) | |||
def test_env_finds_the_correct_executables_for_generic_env(tmp_dir, manager): | def test_env_finds_the_correct_executables_for_generic_env( | |||
tmp_dir: str, manager: EnvManager | ||||
): | ||||
venv_path = Path(tmp_dir) / "Virtual Env" | venv_path = Path(tmp_dir) / "Virtual Env" | |||
child_venv_path = Path(tmp_dir) / "Child Virtual Env" | child_venv_path = Path(tmp_dir) / "Child Virtual Env" | |||
manager.build_venv(str(venv_path)) | manager.build_venv(str(venv_path), with_pip=True) | |||
parent_venv = VirtualEnv(venv_path) | parent_venv = VirtualEnv(venv_path) | |||
manager.build_venv(str(child_venv_path), executable=parent_venv.python) | manager.build_venv( | |||
str(child_venv_path), executable=parent_venv.python, with_pip=True | ||||
) | ||||
venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) | venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) | |||
expected_executable = "python{}.{}{}".format( | expected_executable = ( | |||
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else "" | f"python{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS el | |||
se ''}" | ||||
) | ) | |||
expected_pip_executable = "pip{}.{}{}".format( | expected_pip_executable = ( | |||
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else "" | f"pip{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else | |||
''}" | ||||
) | ) | |||
if WINDOWS: | if WINDOWS: | |||
expected_executable = "python.exe" | expected_executable = "python.exe" | |||
expected_pip_executable = "pip.exe" | expected_pip_executable = "pip.exe" | |||
assert Path(venv.python).name == expected_executable | assert Path(venv.python).name == expected_executable | |||
assert Path(venv.pip).name == expected_pip_executable | assert Path(venv.pip).name == expected_pip_executable | |||
def test_env_finds_fallback_executables_for_generic_env(tmp_dir, manager): | def test_env_finds_fallback_executables_for_generic_env( | |||
tmp_dir: str, manager: EnvManager | ||||
): | ||||
venv_path = Path(tmp_dir) / "Virtual Env" | venv_path = Path(tmp_dir) / "Virtual Env" | |||
child_venv_path = Path(tmp_dir) / "Child Virtual Env" | child_venv_path = Path(tmp_dir) / "Child Virtual Env" | |||
manager.build_venv(str(venv_path)) | manager.build_venv(str(venv_path), with_pip=True) | |||
parent_venv = VirtualEnv(venv_path) | parent_venv = VirtualEnv(venv_path) | |||
manager.build_venv(str(child_venv_path), executable=parent_venv.python) | manager.build_venv( | |||
str(child_venv_path), executable=parent_venv.python, with_pip=True | ||||
) | ||||
venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) | venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) | |||
default_executable = "python" + (".exe" if WINDOWS else "") | default_executable = f"python{'.exe' if WINDOWS else ''}" | |||
major_executable = "python{}{}".format( | major_executable = f"python{sys.version_info[0]}{'.exe' if WINDOWS else ''}" | |||
sys.version_info[0], ".exe" if WINDOWS else "" | minor_executable = ( | |||
) | f"python{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS el | |||
minor_executable = "python{}.{}{}".format( | se ''}" | |||
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else "" | ||||
) | ) | |||
expected_executable = minor_executable | expected_executable = minor_executable | |||
if ( | if ( | |||
venv._bin_dir.joinpath(expected_executable).exists() | venv._bin_dir.joinpath(expected_executable).exists() | |||
and venv._bin_dir.joinpath(major_executable).exists() | and venv._bin_dir.joinpath(major_executable).exists() | |||
): | ): | |||
venv._bin_dir.joinpath(expected_executable).unlink() | venv._bin_dir.joinpath(expected_executable).unlink() | |||
expected_executable = major_executable | expected_executable = major_executable | |||
if ( | if ( | |||
venv._bin_dir.joinpath(expected_executable).exists() | venv._bin_dir.joinpath(expected_executable).exists() | |||
and venv._bin_dir.joinpath(default_executable).exists() | and venv._bin_dir.joinpath(default_executable).exists() | |||
): | ): | |||
venv._bin_dir.joinpath(expected_executable).unlink() | venv._bin_dir.joinpath(expected_executable).unlink() | |||
expected_executable = default_executable | expected_executable = default_executable | |||
default_pip_executable = "pip" + (".exe" if WINDOWS else "") | default_pip_executable = f"pip{'.exe' if WINDOWS else ''}" | |||
major_pip_executable = "pip{}{}".format( | major_pip_executable = f"pip{sys.version_info[0]}{'.exe' if WINDOWS else ''} | |||
sys.version_info[0], ".exe" if WINDOWS else "" | " | |||
) | minor_pip_executable = ( | |||
minor_pip_executable = "pip{}.{}{}".format( | f"pip{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else | |||
sys.version_info[0], sys.version_info[1], ".exe" if WINDOWS else "" | ''}" | |||
) | ) | |||
expected_pip_executable = minor_pip_executable | expected_pip_executable = minor_pip_executable | |||
if ( | if ( | |||
venv._bin_dir.joinpath(expected_pip_executable).exists() | venv._bin_dir.joinpath(expected_pip_executable).exists() | |||
and venv._bin_dir.joinpath(major_pip_executable).exists() | and venv._bin_dir.joinpath(major_pip_executable).exists() | |||
): | ): | |||
venv._bin_dir.joinpath(expected_pip_executable).unlink() | venv._bin_dir.joinpath(expected_pip_executable).unlink() | |||
expected_pip_executable = major_pip_executable | expected_pip_executable = major_pip_executable | |||
if ( | if ( | |||
skipping to change at line 956 | skipping to change at line 1262 | |||
if not venv._bin_dir.joinpath(expected_executable).exists(): | if not venv._bin_dir.joinpath(expected_executable).exists(): | |||
expected_executable = default_executable | expected_executable = default_executable | |||
if not venv._bin_dir.joinpath(expected_pip_executable).exists(): | if not venv._bin_dir.joinpath(expected_pip_executable).exists(): | |||
expected_pip_executable = default_pip_executable | expected_pip_executable = default_pip_executable | |||
venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) | venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) | |||
assert Path(venv.python).name == expected_executable | assert Path(venv.python).name == expected_executable | |||
assert Path(venv.pip).name == expected_pip_executable | assert Path(venv.pip).name == expected_pip_executable | |||
def test_create_venv_accepts_fallback_version_w_nonzero_patchlevel( | ||||
manager: EnvManager, | ||||
poetry: Poetry, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
config_virtualenvs_path: Path, | ||||
): | ||||
if "VIRTUAL_ENV" in os.environ: | ||||
del os.environ["VIRTUAL_ENV"] | ||||
poetry.package.python_versions = "~3.5.1" | ||||
venv_name = manager.generate_env_name("simple-project", str(poetry.file.pare | ||||
nt)) | ||||
check_output = mocker.patch( | ||||
"subprocess.check_output", | ||||
side_effect=lambda cmd, *args, **kwargs: str( | ||||
"3.5.12" if "python3.5" in cmd else "3.7.1" | ||||
), | ||||
) | ||||
m = mocker.patch( | ||||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw | ||||
args: "" | ||||
) | ||||
manager.create_venv(NullIO()) | ||||
assert check_output.called | ||||
m.assert_called_with( | ||||
config_virtualenvs_path / f"{venv_name}-py3.5", | ||||
executable="python3.5", | ||||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="simple-project-py3.5", | ||||
) | ||||
def test_generate_env_name_ignores_case_for_case_insensitive_fs(tmp_dir: str): | ||||
venv_name1 = EnvManager.generate_env_name("simple-project", "MyDiR") | ||||
venv_name2 = EnvManager.generate_env_name("simple-project", "mYdIr") | ||||
if sys.platform == "win32": | ||||
assert venv_name1 == venv_name2 | ||||
else: | ||||
assert venv_name1 != venv_name2 | ||||
def test_generate_env_name_uses_real_path(tmp_dir: str, mocker: MockerFixture): | ||||
mocker.patch("os.path.realpath", return_value="the_real_dir") | ||||
venv_name1 = EnvManager.generate_env_name("simple-project", "the_real_dir") | ||||
venv_name2 = EnvManager.generate_env_name("simple-project", "linked_dir") | ||||
assert venv_name1 == venv_name2 | ||||
@pytest.fixture() | ||||
def extended_without_setup_poetry() -> Poetry: | ||||
poetry = Factory().create_poetry( | ||||
Path(__file__).parent.parent / "fixtures" / "extended_project_without_se | ||||
tup" | ||||
) | ||||
return poetry | ||||
def test_build_environment_called_build_script_specified( | ||||
mocker: MockerFixture, extended_without_setup_poetry: Poetry, tmp_dir: str | ||||
): | ||||
project_env = MockEnv(path=Path(tmp_dir) / "project") | ||||
ephemeral_env = MockEnv(path=Path(tmp_dir) / "ephemeral") | ||||
mocker.patch( | ||||
"poetry.utils.env.ephemeral_environment" | ||||
).return_value.__enter__.return_value = ephemeral_env | ||||
with build_environment(extended_without_setup_poetry, project_env) as env: | ||||
assert env == ephemeral_env | ||||
assert env.executed == [ | ||||
[ | ||||
"python", | ||||
env.pip_embedded, | ||||
"install", | ||||
"--disable-pip-version-check", | ||||
"--ignore-installed", | ||||
*extended_without_setup_poetry.pyproject.build_system.requires, | ||||
] | ||||
] | ||||
def test_build_environment_not_called_without_build_script_specified( | ||||
mocker: MockerFixture, poetry: Poetry, tmp_dir: str | ||||
): | ||||
project_env = MockEnv(path=Path(tmp_dir) / "project") | ||||
ephemeral_env = MockEnv(path=Path(tmp_dir) / "ephemeral") | ||||
mocker.patch( | ||||
"poetry.utils.env.ephemeral_environment" | ||||
).return_value.__enter__.return_value = ephemeral_env | ||||
with build_environment(poetry, project_env) as env: | ||||
assert env == project_env | ||||
assert not env.executed | ||||
def test_create_venv_project_name_empty_sets_correct_prompt( | ||||
project_factory: ProjectFactory, | ||||
config: Config, | ||||
mocker: MockerFixture, | ||||
config_virtualenvs_path: Path, | ||||
): | ||||
if "VIRTUAL_ENV" in os.environ: | ||||
del os.environ["VIRTUAL_ENV"] | ||||
fixture = Path(__file__).parent.parent / "fixtures" / "no_name_project" | ||||
poetry = project_factory("no", source=fixture) | ||||
manager = EnvManager(poetry) | ||||
poetry.package.python_versions = "^3.7" | ||||
venv_name = manager.generate_env_name("", str(poetry.file.parent)) | ||||
mocker.patch("sys.version_info", (2, 7, 16)) | ||||
mocker.patch( | ||||
"subprocess.check_output", | ||||
side_effect=check_output_wrapper(Version.parse("3.7.5")), | ||||
) | ||||
m = mocker.patch( | ||||
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kw | ||||
args: "" | ||||
) | ||||
manager.create_venv(NullIO()) | ||||
m.assert_called_with( | ||||
config_virtualenvs_path / f"{venv_name}-py3.7", | ||||
executable="python3", | ||||
flags={ | ||||
"always-copy": False, | ||||
"system-site-packages": False, | ||||
"no-pip": False, | ||||
"no-setuptools": False, | ||||
}, | ||||
prompt="virtualenv-py3.7", | ||||
) | ||||
End of changes. 146 change blocks. | ||||
242 lines changed or deleted | 558 lines changed or added |