Commit 25aae995 by Sébastien Eustace Committed by GitHub

Merge pull request #4011 from abn/env/default-with-pip

env: default to enabling pip/wheels/setuptools
parents 16031f05 f0408d61
...@@ -161,7 +161,7 @@ python-versions = ">=3.6, <3.7" ...@@ -161,7 +161,7 @@ python-versions = ">=3.6, <3.7"
[[package]] [[package]]
name = "deepdiff" name = "deepdiff"
version = "5.2.3" version = "5.5.0"
description = "Deep Difference and Search of any Python object/data." description = "Deep Difference and Search of any Python object/data."
category = "dev" category = "dev"
optional = false optional = false
...@@ -171,7 +171,7 @@ python-versions = ">=3.6" ...@@ -171,7 +171,7 @@ python-versions = ">=3.6"
ordered-set = "4.0.2" ordered-set = "4.0.2"
[package.extras] [package.extras]
cli = ["click (==7.1.2)", "pyyaml (==5.3.1)", "toml (==0.10.2)", "clevercsv (==0.6.6)"] cli = ["click (==7.1.2)", "pyyaml (==5.4)", "toml (==0.10.2)", "clevercsv (==0.6.7)"]
[[package]] [[package]]
name = "distlib" name = "distlib"
...@@ -225,7 +225,7 @@ python-versions = ">=3" ...@@ -225,7 +225,7 @@ python-versions = ">=3"
[[package]] [[package]]
name = "identify" name = "identify"
version = "2.2.1" version = "2.2.4"
description = "File identification library for Python" description = "File identification library for Python"
category = "dev" category = "dev"
optional = false optional = false
...@@ -327,7 +327,7 @@ python-versions = "*" ...@@ -327,7 +327,7 @@ python-versions = "*"
[[package]] [[package]]
name = "nodeenv" name = "nodeenv"
version = "1.5.0" version = "1.6.0"
description = "Node.js virtual environment builder" description = "Node.js virtual environment builder"
category = "dev" category = "dev"
optional = false optional = false
...@@ -390,7 +390,7 @@ dev = ["pre-commit", "tox"] ...@@ -390,7 +390,7 @@ dev = ["pre-commit", "tox"]
[[package]] [[package]]
name = "poetry-core" name = "poetry-core"
version = "1.1.0a2" version = "1.1.0a3"
description = "Poetry PEP 517 Build Backend" description = "Poetry PEP 517 Build Backend"
category = "main" category = "main"
optional = false optional = false
...@@ -398,18 +398,18 @@ python-versions = "^3.6" ...@@ -398,18 +398,18 @@ python-versions = "^3.6"
develop = false develop = false
[package.dependencies] [package.dependencies]
dataclasses = {version = "^0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""} dataclasses = {version = ">=0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""}
importlib-metadata = {version = "^1.7.0", markers = "python_version >= \"3.5\" and python_version < \"3.8\""} importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""}
[package.source] [package.source]
type = "git" type = "git"
url = "https://github.com/python-poetry/poetry-core.git" url = "https://github.com/python-poetry/poetry-core.git"
reference = "master" reference = "master"
resolved_reference = "d3e60732ce9bd4f30dee3e594405fe6a80163b7e" resolved_reference = "3f718c55fcda63d9bd88b8fc612970c24fc9af25"
[[package]] [[package]]
name = "pre-commit" name = "pre-commit"
version = "2.11.1" version = "2.12.1"
description = "A framework for managing and maintaining multi-language pre-commit hooks." description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev" category = "dev"
optional = false optional = false
...@@ -665,7 +665,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] ...@@ -665,7 +665,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]] [[package]]
name = "virtualenv" name = "virtualenv"
version = "20.4.3" version = "20.4.4"
description = "Virtual Python Environment builder" description = "Virtual Python Environment builder"
category = "main" category = "main"
optional = false optional = false
...@@ -873,8 +873,8 @@ dataclasses = [ ...@@ -873,8 +873,8 @@ dataclasses = [
{file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"},
] ]
deepdiff = [ deepdiff = [
{file = "deepdiff-5.2.3-py3-none-any.whl", hash = "sha256:3d3da4bd7e01fb5202088658ed26427104c748dda56a0ecfac9ce9a1d2d00844"}, {file = "deepdiff-5.5.0-py3-none-any.whl", hash = "sha256:e054fed9dfe0d83d622921cbb3a3d0b3a6dd76acd2b6955433a0a2d35147774a"},
{file = "deepdiff-5.2.3.tar.gz", hash = "sha256:ae2cb98353309f93fbfdda4d77adb08fb303314d836bb6eac3d02ed71a10b40e"}, {file = "deepdiff-5.5.0.tar.gz", hash = "sha256:dd79b81c2d84bfa33aa9d94d456b037b68daff6bb87b80dfaa1eca04da68b349"},
] ]
distlib = [ distlib = [
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
...@@ -896,8 +896,8 @@ httpretty = [ ...@@ -896,8 +896,8 @@ httpretty = [
{file = "httpretty-1.0.5.tar.gz", hash = "sha256:e53c927c4d3d781a0761727f1edfad64abef94e828718e12b672a678a8b3e0b5"}, {file = "httpretty-1.0.5.tar.gz", hash = "sha256:e53c927c4d3d781a0761727f1edfad64abef94e828718e12b672a678a8b3e0b5"},
] ]
identify = [ identify = [
{file = "identify-2.2.1-py2.py3-none-any.whl", hash = "sha256:9cc5f58996cd359b7b72f0a5917d8639de5323917e6952a3bfbf36301b576f40"}, {file = "identify-2.2.4-py2.py3-none-any.whl", hash = "sha256:ad9f3fa0c2316618dc4d840f627d474ab6de106392a4f00221820200f490f5a8"},
{file = "identify-2.2.1.tar.gz", hash = "sha256:1cfb05b578de996677836d5a2dde14b3dffde313cf7d2b3e793a0787a36e26dd"}, {file = "identify-2.2.4.tar.gz", hash = "sha256:9bcc312d4e2fa96c7abebcdfb1119563b511b5e3985ac52f60d9116277865b2e"},
] ]
idna = [ idna = [
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
...@@ -958,8 +958,8 @@ msgpack = [ ...@@ -958,8 +958,8 @@ msgpack = [
{file = "msgpack-1.0.2.tar.gz", hash = "sha256:fae04496f5bc150eefad4e9571d1a76c55d021325dcd484ce45065ebbdd00984"}, {file = "msgpack-1.0.2.tar.gz", hash = "sha256:fae04496f5bc150eefad4e9571d1a76c55d021325dcd484ce45065ebbdd00984"},
] ]
nodeenv = [ nodeenv = [
{file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
{file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
] ]
ordered-set = [ ordered-set = [
{file = "ordered-set-4.0.2.tar.gz", hash = "sha256:ba93b2df055bca202116ec44b9bead3df33ea63a7d5827ff8e16738b97f33a95"}, {file = "ordered-set-4.0.2.tar.gz", hash = "sha256:ba93b2df055bca202116ec44b9bead3df33ea63a7d5827ff8e16738b97f33a95"},
...@@ -982,8 +982,8 @@ pluggy = [ ...@@ -982,8 +982,8 @@ pluggy = [
] ]
poetry-core = [] poetry-core = []
pre-commit = [ pre-commit = [
{file = "pre_commit-2.11.1-py2.py3-none-any.whl", hash = "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b"}, {file = "pre_commit-2.12.1-py2.py3-none-any.whl", hash = "sha256:70c5ec1f30406250b706eda35e868b87e3e4ba099af8787e3e8b4b01e84f4712"},
{file = "pre_commit-2.11.1.tar.gz", hash = "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af"}, {file = "pre_commit-2.12.1.tar.gz", hash = "sha256:900d3c7e1bf4cf0374bb2893c24c23304952181405b4d88c9c40b72bda1bb8a9"},
] ]
ptyprocess = [ ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
...@@ -1087,8 +1087,8 @@ urllib3 = [ ...@@ -1087,8 +1087,8 @@ urllib3 = [
{file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"},
] ]
virtualenv = [ virtualenv = [
{file = "virtualenv-20.4.3-py2.py3-none-any.whl", hash = "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c"}, {file = "virtualenv-20.4.4-py2.py3-none-any.whl", hash = "sha256:a935126db63128861987a7d5d30e23e8ec045a73840eeccb467c148514e29535"},
{file = "virtualenv-20.4.3.tar.gz", hash = "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107"}, {file = "virtualenv-20.4.4.tar.gz", hash = "sha256:09c61377ef072f43568207dc8e46ddeac6bcdcaf288d49011bda0e7f4d38c4a2"},
] ]
wcwidth = [ wcwidth = [
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
......
...@@ -670,7 +670,13 @@ class Executor: ...@@ -670,7 +670,13 @@ class Executor:
archive = self._chef.prepare(archive) archive = self._chef.prepare(archive)
if package.files: if package.files:
archive_hash = "sha256:" + FileDependency(package.name, archive).hash() archive_hash = (
"sha256:"
+ FileDependency(
package.name,
Path(archive.path) if isinstance(archive, Link) else archive,
).hash()
)
if archive_hash not in {f["hash"] for f in package.files}: if archive_hash not in {f["hash"] for f in package.files}:
raise RuntimeError( raise RuntimeError(
f"Invalid hash for {package} using archive {archive.name}" f"Invalid hash for {package} using archive {archive.name}"
......
...@@ -63,10 +63,31 @@ class Solver: ...@@ -63,10 +63,31 @@ class Solver:
self._overrides = [] self._overrides = []
self._remove_untracked = remove_untracked self._remove_untracked = remove_untracked
self._preserved_package_names = None
@property @property
def provider(self) -> Provider: def provider(self) -> Provider:
return self._provider return self._provider
@property
def preserved_package_names(self):
if self._preserved_package_names is None:
self._preserved_package_names = {
self._package.name,
*Provider.UNSAFE_PACKAGES,
}
deps = {package.name for package in self._locked.packages}
# preserve pip/setuptools/wheel when not managed by poetry, this is so
# to avoid externally managed virtual environments causing unnecessary
# removals.
for name in {"pip", "wheel", "setuptools"}:
if name not in deps:
self._preserved_package_names.add(name)
return self._preserved_package_names
@contextmanager @contextmanager
def use_environment(self, env: Env) -> None: def use_environment(self, env: Env) -> None:
with self.provider.use_environment(env): with self.provider.use_environment(env):
...@@ -190,11 +211,9 @@ class Solver: ...@@ -190,11 +211,9 @@ class Solver:
locked_names = {locked.name for locked in self._locked.packages} locked_names = {locked.name for locked in self._locked.packages}
for installed in self._installed.packages: for installed in self._installed.packages:
if installed.name == self._package.name: if installed.name in self.preserved_package_names:
continue
if installed.name in Provider.UNSAFE_PACKAGES:
# Never remove pip, setuptools etc.
continue continue
if installed.name not in locked_names: if installed.name not in locked_names:
operations.append(Uninstall(installed)) operations.append(Uninstall(installed))
......
...@@ -877,13 +877,8 @@ class EnvManager: ...@@ -877,13 +877,8 @@ class EnvManager:
io.write_line( io.write_line(
"Creating virtualenv <c1>{}</> in {}".format(name, str(venv_path)) "Creating virtualenv <c1>{}</> in {}".format(name, str(venv_path))
) )
self.build_venv(
venv,
executable=executable,
flags=self._poetry.config.get("virtualenvs.options"),
)
else: else:
create_venv = False
if force: if force:
if not env.is_sane(): if not env.is_sane():
io.write_line( io.write_line(
...@@ -895,14 +890,23 @@ class EnvManager: ...@@ -895,14 +890,23 @@ class EnvManager:
"Recreating virtualenv <c1>{}</> in {}".format(name, str(venv)) "Recreating virtualenv <c1>{}</> in {}".format(name, str(venv))
) )
self.remove_venv(venv) self.remove_venv(venv)
self.build_venv( create_venv = True
venv,
executable=executable,
flags=self._poetry.config.get("virtualenvs.options"),
)
elif io.is_very_verbose(): elif io.is_very_verbose():
io.write_line(f"Virtualenv <c1>{name}</> already exists.") io.write_line(f"Virtualenv <c1>{name}</> already exists.")
if create_venv:
self.build_venv(
venv,
executable=executable,
flags=self._poetry.config.get("virtualenvs.options"),
# TODO: in a future version switch remove pip/setuptools/wheel
# poetry does not need them these exists today to not break developer
# environment assumptions
with_pip=True,
with_setuptools=True,
with_wheel=True,
)
# venv detection: # venv detection:
# stdlib venv may symlink sys.executable, so we can't use realpath. # stdlib venv may symlink sys.executable, so we can't use realpath.
# but others can symlink *to* the venv Python, # but others can symlink *to* the venv Python,
...@@ -927,12 +931,29 @@ class EnvManager: ...@@ -927,12 +931,29 @@ class EnvManager:
path: Union[Path, str], path: Union[Path, str],
executable: Optional[Union[str, Path]] = None, executable: Optional[Union[str, Path]] = None,
flags: Dict[str, bool] = None, flags: Dict[str, bool] = None,
with_pip: bool = False, with_pip: Optional[bool] = None,
with_wheel: Optional[bool] = None, with_wheel: Optional[bool] = None,
with_setuptools: Optional[bool] = None, with_setuptools: Optional[bool] = None,
) -> virtualenv.run.session.Session: ) -> virtualenv.run.session.Session:
flags = flags or {} flags = flags or {}
flags["no-pip"] = (
not with_pip if with_pip is not None else flags.pop("no-pip", True)
)
flags["no-setuptools"] = (
not with_setuptools
if with_setuptools is not None
else flags.pop("no-setuptools", True)
)
# we want wheels to be enabled when pip is required and it has not been explicitly disabled
flags["no-wheel"] = (
not with_wheel
if with_wheel is not None
else flags.pop("no-wheel", flags["no-pip"])
)
if isinstance(executable, Path): if isinstance(executable, Path):
executable = executable.resolve().as_posix() executable = executable.resolve().as_posix()
...@@ -943,20 +964,6 @@ class EnvManager: ...@@ -943,20 +964,6 @@ class EnvManager:
executable or sys.executable, executable or sys.executable,
] ]
if not with_pip:
args.append("--no-pip")
else:
if with_wheel is None:
# we want wheels to be enabled when pip is required and it has
# not been explicitly disabled
with_wheel = True
if with_wheel is None or not with_wheel:
args.append("--no-wheel")
if with_setuptools is None or not with_setuptools:
args.append("--no-setuptools")
for flag, value in flags.items(): for flag, value in flags.items():
if value is True: if value is True:
args.append(f"--{flag}") args.append(f"--{flag}")
...@@ -1039,6 +1046,8 @@ class Env: ...@@ -1039,6 +1046,8 @@ class Env:
self._platlib = None self._platlib = None
self._script_dirs = None self._script_dirs = None
self._embedded_pip_path = None
@property @property
def path(self) -> Path: def path(self) -> Path:
return self._path return self._path
...@@ -1075,6 +1084,12 @@ class Env: ...@@ -1075,6 +1084,12 @@ class Env:
).path ).path
@property @property
def pip_embedded(self) -> str:
if self._embedded_pip_path is None:
self._embedded_pip_path = str(self.get_embedded_wheel("pip") / "pip")
return self._embedded_pip_path
@property
def pip(self) -> str: def pip(self) -> str:
""" """
Path to current pip executable Path to current pip executable
...@@ -1082,7 +1097,7 @@ class Env: ...@@ -1082,7 +1097,7 @@ class Env:
# we do not use as_posix() here due to issues with windows pathlib2 implementation # we do not use as_posix() here due to issues with windows pathlib2 implementation
path = self._bin("pip") path = self._bin("pip")
if not Path(path).exists(): if not Path(path).exists():
return str(self.get_embedded_wheel("pip") / "pip") return str(self.pip_embedded)
return path return path
@property @property
...@@ -1187,7 +1202,7 @@ class Env: ...@@ -1187,7 +1202,7 @@ class Env:
def get_marker_env(self) -> Dict[str, Any]: def get_marker_env(self) -> Dict[str, Any]:
raise NotImplementedError() raise NotImplementedError()
def get_pip_command(self) -> List[str]: def get_pip_command(self, embedded: bool = False) -> List[str]:
raise NotImplementedError() raise NotImplementedError()
def get_supported_tags(self) -> List[Tag]: def get_supported_tags(self) -> List[Tag]:
...@@ -1208,16 +1223,20 @@ class Env: ...@@ -1208,16 +1223,20 @@ class Env:
""" """
return True return True
def run(self, bin: str, *args: str, **kwargs: Any) -> Union[str, int]: def get_command_from_bin(self, bin: str) -> List[str]:
if bin == "pip": if bin == "pip":
return self.run_pip(*args, **kwargs) # when pip is required we need to ensure that we fallback to
# embedded pip when pip is not available in the environment
return self.get_pip_command()
return [self._bin(bin)]
bin = self._bin(bin) def run(self, bin: str, *args: str, **kwargs: Any) -> Union[str, int]:
cmd = [bin] + list(args) cmd = self.get_command_from_bin(bin) + list(args)
return self._run(cmd, **kwargs) return self._run(cmd, **kwargs)
def run_pip(self, *args: str, **kwargs: Any) -> Union[int, str]: def run_pip(self, *args: str, **kwargs: Any) -> Union[int, str]:
pip = self.get_pip_command() pip = self.get_pip_command(embedded=True)
cmd = pip + list(args) cmd = pip + list(args)
return self._run(cmd, **kwargs) return self._run(cmd, **kwargs)
...@@ -1230,6 +1249,7 @@ class Env: ...@@ -1230,6 +1249,7 @@ class Env:
""" """
call = kwargs.pop("call", False) call = kwargs.pop("call", False)
input_ = kwargs.pop("input_", None) input_ = kwargs.pop("input_", None)
env = kwargs.pop("env", {k: v for k, v in os.environ.items()})
try: try:
if self._is_windows: if self._is_windows:
...@@ -1248,10 +1268,10 @@ class Env: ...@@ -1248,10 +1268,10 @@ class Env:
**kwargs, **kwargs,
).stdout ).stdout
elif call: elif call:
return subprocess.call(cmd, stderr=subprocess.STDOUT, **kwargs) return subprocess.call(cmd, stderr=subprocess.STDOUT, env=env, **kwargs)
else: else:
output = subprocess.check_output( output = subprocess.check_output(
cmd, stderr=subprocess.STDOUT, **kwargs cmd, stderr=subprocess.STDOUT, env=env, **kwargs
) )
except CalledProcessError as e: except CalledProcessError as e:
raise EnvCommandError(e, input=input_) raise EnvCommandError(e, input=input_)
...@@ -1259,17 +1279,13 @@ class Env: ...@@ -1259,17 +1279,13 @@ class Env:
return decode(output) return decode(output)
def execute(self, bin: str, *args: str, **kwargs: Any) -> Optional[int]: def execute(self, bin: str, *args: str, **kwargs: Any) -> Optional[int]:
if bin == "pip": command = self.get_command_from_bin(bin) + list(args)
return self.run_pip(*args, **kwargs)
bin = self._bin(bin)
env = kwargs.pop("env", {k: v for k, v in os.environ.items()}) env = kwargs.pop("env", {k: v for k, v in os.environ.items()})
if not self._is_windows: if not self._is_windows:
args = [bin] + list(args) return os.execvpe(command[0], command, env=env)
return os.execvpe(bin, args, env=env)
else: else:
exe = subprocess.Popen([bin] + list(args), env=env, **kwargs) exe = subprocess.Popen([command[0]] + command[1:], env=env, **kwargs)
exe.communicate() exe.communicate()
return exe.returncode return exe.returncode
...@@ -1337,10 +1353,10 @@ class SystemEnv(Env): ...@@ -1337,10 +1353,10 @@ class SystemEnv(Env):
def get_python_implementation(self) -> str: def get_python_implementation(self) -> str:
return platform.python_implementation() return platform.python_implementation()
def get_pip_command(self) -> List[str]: def get_pip_command(self, embedded: bool = False) -> List[str]:
# If we're not in a venv, assume the interpreter we're running on # If we're not in a venv, assume the interpreter we're running on
# has a pip and use that # has a pip and use that
return [sys.executable, self.pip] return [sys.executable, self.pip_embedded if embedded else self.pip]
def get_paths(self) -> Dict[str, str]: def get_paths(self) -> Dict[str, str]:
# We can't use sysconfig.get_paths() because # We can't use sysconfig.get_paths() because
...@@ -1444,10 +1460,10 @@ class VirtualEnv(Env): ...@@ -1444,10 +1460,10 @@ class VirtualEnv(Env):
def get_python_implementation(self) -> str: def get_python_implementation(self) -> str:
return self.marker_env["platform_python_implementation"] return self.marker_env["platform_python_implementation"]
def get_pip_command(self) -> List[str]: def get_pip_command(self, embedded: bool = False) -> List[str]:
# We're in a virtualenv that is known to be sane, # We're in a virtualenv that is known to be sane,
# so assume that we have a functional pip # so assume that we have a functional pip
return [self._bin("python"), self.pip] return [self._bin("python"), self.pip_embedded if embedded else self.pip]
def get_supported_tags(self) -> List[Tag]: def get_supported_tags(self) -> List[Tag]:
file_path = Path(packaging.tags.__file__) file_path = Path(packaging.tags.__file__)
...@@ -1559,8 +1575,8 @@ class NullEnv(SystemEnv): ...@@ -1559,8 +1575,8 @@ class NullEnv(SystemEnv):
self._execute = execute self._execute = execute
self.executed = [] self.executed = []
def get_pip_command(self) -> List[str]: def get_pip_command(self, embedded: bool = False) -> List[str]:
return [self._bin("python"), self.pip] return [self._bin("python"), self.pip_embedded if embedded else self.pip]
def _run(self, cmd: List[str], **kwargs: Any) -> int: def _run(self, cmd: List[str], **kwargs: Any) -> int:
self.executed.append(cmd) self.executed.append(cmd)
......
...@@ -53,7 +53,7 @@ def pip_install( ...@@ -53,7 +53,7 @@ def pip_install(
executable=environment.python, with_pip=True, with_setuptools=True executable=environment.python, with_pip=True, with_setuptools=True
) as env: ) as env:
return environment.run( return environment.run(
env._bin("pip"), *env.get_pip_command(),
*args, *args,
env={**os.environ, "PYTHONPATH": str(env.purelib)}, env={**os.environ, "PYTHONPATH": str(env.purelib)},
) )
......
...@@ -100,6 +100,15 @@ def config(config_source, auth_config_source, mocker): ...@@ -100,6 +100,15 @@ def config(config_source, auth_config_source, mocker):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def mock_user_config_dir(mocker):
config_dir = tempfile.mkdtemp(prefix="poetry_config_")
mocker.patch("poetry.locations.CONFIG_DIR", new=config_dir)
mocker.patch("poetry.factory.CONFIG_DIR", new=config_dir)
yield
shutil.rmtree(config_dir, ignore_errors=True)
@pytest.fixture(autouse=True)
def download_mock(mocker): def download_mock(mocker):
# Patch download to not download anything but to just copy from fixtures # Patch download to not download anything but to just copy from fixtures
mocker.patch("poetry.utils.helpers.download_file", new=mock_download) mocker.patch("poetry.utils.helpers.download_file", new=mock_download)
......
...@@ -55,6 +55,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( ...@@ -55,6 +55,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(
venv_py37, venv_py37,
executable="python3.7", executable="python3.7",
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
envs_file = TOMLFile(venv_cache / "envs.toml") envs_file = TOMLFile(venv_cache / "envs.toml")
......
...@@ -217,7 +217,7 @@ def test_info_setup_missing_mandatory_should_trigger_pep517( ...@@ -217,7 +217,7 @@ def test_info_setup_missing_mandatory_should_trigger_pep517(
except PackageInfoError: except PackageInfoError:
assert spy.call_count == 3 assert spy.call_count == 3
else: else:
assert spy.call_count == 1 assert spy.call_count == 2
def test_info_prefer_poetry_config_over_egg_info(): def test_info_prefer_poetry_config_over_egg_info():
......
from __future__ import unicode_literals from __future__ import unicode_literals
import itertools
import json import json
import sys import sys
...@@ -35,6 +36,9 @@ from tests.repositories.test_legacy_repository import ( ...@@ -35,6 +36,9 @@ from tests.repositories.test_legacy_repository import (
from tests.repositories.test_pypi_repository import MockRepository from tests.repositories.test_pypi_repository import MockRepository
RESERVED_PACKAGES = ("pip", "setuptools", "wheel")
class Installer(BaseInstaller): class Installer(BaseInstaller):
def _get_installer(self): def _get_installer(self):
return NoopInstaller() return NoopInstaller()
...@@ -367,59 +371,88 @@ def test_run_install_no_dev_and_dev_only(installer, locker, repo, package, insta ...@@ -367,59 +371,88 @@ def test_run_install_no_dev_and_dev_only(installer, locker, repo, package, insta
assert 1 == installer.executor.removals_count assert 1 == installer.executor.removals_count
def test_run_install_remove_untracked(installer, locker, repo, package, installed): @pytest.mark.parametrize(
"managed_reserved_package_names",
[
i
for i in itertools.chain(
[tuple()],
itertools.permutations(RESERVED_PACKAGES, 1),
itertools.permutations(RESERVED_PACKAGES, 2),
[RESERVED_PACKAGES],
)
],
)
def test_run_install_remove_untracked(
managed_reserved_package_names, installer, locker, repo, package, installed
):
package_a = get_package("a", "1.0")
package_b = get_package("b", "1.1")
package_c = get_package("c", "1.2")
package_pip = get_package("pip", "20.0.0")
package_setuptools = get_package("setuptools", "20.0.0")
package_wheel = get_package("wheel", "20.0.0")
all_packages = [
package_a,
package_b,
package_c,
package_pip,
package_setuptools,
package_wheel,
]
managed_reserved_packages = [
pkg for pkg in all_packages if pkg.name in managed_reserved_package_names
]
locked_packages = [package_a, *managed_reserved_packages]
for pkg in all_packages:
repo.add_package(pkg)
installed.add_package(pkg)
installed.add_package(package) # Root package never removed.
package.add_dependency(Factory.create_dependency(package_a.name, package_a.version))
locker.locked(True) locker.locked(True)
locker.mock_lock_data( locker.mock_lock_data(
{ {
"package": [ "package": [
{ {
"name": "a", "name": pkg.name,
"version": "1.0", "version": pkg.version,
"category": "main", "category": "main",
"optional": False, "optional": False,
"platform": "*", "platform": "*",
"python-versions": "*", "python-versions": "*",
"checksum": [], "checksum": [],
} }
for pkg in locked_packages
], ],
"metadata": { "metadata": {
"python-versions": "*", "python-versions": "*",
"platform": "*", "platform": "*",
"content-hash": "123456789", "content-hash": "123456789",
"hashes": {"a": []}, "hashes": {pkg.name: [] for pkg in locked_packages},
}, },
} }
) )
package_a = get_package("a", "1.0")
package_b = get_package("b", "1.1")
package_c = get_package("c", "1.2")
package_pip = get_package("pip", "20.0.0")
package_setuptools = get_package("setuptools", "20.0.0")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_pip)
repo.add_package(package_setuptools)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
installed.add_package(package_pip)
installed.add_package(package_setuptools)
installed.add_package(package) # Root package never removed.
package.add_dependency(Factory.create_dependency("A", "~1.0"))
installer.dev_mode(True).remove_untracked(True) installer.dev_mode(True).remove_untracked(True)
installer.run() installer.run()
assert 0 == installer.executor.installations_count assert 0 == installer.executor.installations_count
assert 0 == installer.executor.updates_count assert 0 == installer.executor.updates_count
assert 4 == installer.executor.removals_count assert 2 + len(managed_reserved_packages) == installer.executor.removals_count
assert {"b", "c", "pip", "setuptools"} == set(
r.name for r in installer.executor.removals expected_removals = {
) package_b.name,
package_c.name,
*managed_reserved_package_names,
}
assert expected_removals == set(r.name for r in installer.executor.removals)
def test_run_whitelist_add(installer, locker, repo, package): def test_run_whitelist_add(installer, locker, repo, package):
......
from __future__ import unicode_literals from __future__ import unicode_literals
import itertools
import sys import sys
from pathlib import Path from pathlib import Path
...@@ -28,6 +29,9 @@ from tests.repositories.test_legacy_repository import ( ...@@ -28,6 +29,9 @@ from tests.repositories.test_legacy_repository import (
from tests.repositories.test_pypi_repository import MockRepository from tests.repositories.test_pypi_repository import MockRepository
RESERVED_PACKAGES = ("pip", "setuptools", "wheel")
class Installer(BaseInstaller): class Installer(BaseInstaller):
def _get_installer(self): def _get_installer(self):
return NoopInstaller() return NoopInstaller()
...@@ -292,49 +296,73 @@ def test_run_install_no_dev(installer, locker, repo, package, installed): ...@@ -292,49 +296,73 @@ def test_run_install_no_dev(installer, locker, repo, package, installed):
assert len(removals) == 1 assert len(removals) == 1
def test_run_install_remove_untracked(installer, locker, repo, package, installed): @pytest.mark.parametrize(
"managed_reserved_package_names",
[
i
for i in itertools.chain(
[tuple()],
itertools.permutations(RESERVED_PACKAGES, 1),
itertools.permutations(RESERVED_PACKAGES, 2),
[RESERVED_PACKAGES],
)
],
)
def test_run_install_remove_untracked(
managed_reserved_package_names, installer, locker, repo, package, installed
):
package_a = get_package("a", "1.0")
package_b = get_package("b", "1.1")
package_c = get_package("c", "1.2")
package_pip = get_package("pip", "20.0.0")
package_setuptools = get_package("setuptools", "20.0.0")
package_wheel = get_package("wheel", "20.0.0")
all_packages = [
package_a,
package_b,
package_c,
package_pip,
package_setuptools,
package_wheel,
]
managed_reserved_packages = [
pkg for pkg in all_packages if pkg.name in managed_reserved_package_names
]
locked_packages = [package_a, *managed_reserved_packages]
for pkg in all_packages:
repo.add_package(pkg)
installed.add_package(pkg)
installed.add_package(package) # Root package never removed.
package.add_dependency(Factory.create_dependency(package_a.name, package_a.version))
locker.locked(True) locker.locked(True)
locker.mock_lock_data( locker.mock_lock_data(
{ {
"package": [ "package": [
{ {
"name": "a", "name": pkg.name,
"version": "1.0", "version": pkg.version,
"category": "main", "category": "main",
"optional": False, "optional": False,
"platform": "*", "platform": "*",
"python-versions": "*", "python-versions": "*",
"checksum": [], "checksum": [],
} }
for pkg in locked_packages
], ],
"metadata": { "metadata": {
"python-versions": "*", "python-versions": "*",
"platform": "*", "platform": "*",
"content-hash": "123456789", "content-hash": "123456789",
"hashes": {"a": []}, "hashes": {pkg.name: [] for pkg in locked_packages},
}, },
} }
) )
package_a = get_package("a", "1.0")
package_b = get_package("b", "1.1")
package_c = get_package("c", "1.2")
package_pip = get_package("pip", "20.0.0")
package_setuptools = get_package("setuptools", "20.0.0")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_pip)
repo.add_package(package_setuptools)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
installed.add_package(package_pip)
installed.add_package(package_setuptools)
installed.add_package(package) # Root package never removed.
package.add_dependency(Factory.create_dependency("A", "~1.0"))
installer.dev_mode(True).remove_untracked(True) installer.dev_mode(True).remove_untracked(True)
installer.run() installer.run()
...@@ -346,7 +374,12 @@ def test_run_install_remove_untracked(installer, locker, repo, package, installe ...@@ -346,7 +374,12 @@ def test_run_install_remove_untracked(installer, locker, repo, package, installe
assert len(updates) == 0 assert len(updates) == 0
removals = installer.installer.removals removals = installer.installer.removals
assert set(r.name for r in removals) == {"b", "c", "pip", "setuptools"} expected_removals = {
package_b.name,
package_c.name,
*managed_reserved_package_names,
}
assert set(r.name for r in removals) == expected_removals
def test_run_whitelist_add(installer, locker, repo, package): def test_run_whitelist_add(installer, locker, repo, package):
......
...@@ -153,7 +153,7 @@ from bar import baz ...@@ -153,7 +153,7 @@ from bar import baz
if __name__ == '__main__': if __name__ == '__main__':
baz.boom.bim() baz.boom.bim()
""".format( """.format(
python=tmp_venv._bin("python") python=tmp_venv.python
) )
assert baz_script == tmp_venv._bin_dir.joinpath("baz").read_text() assert baz_script == tmp_venv._bin_dir.joinpath("baz").read_text()
...@@ -165,7 +165,7 @@ from foo import bar ...@@ -165,7 +165,7 @@ from foo import bar
if __name__ == '__main__': if __name__ == '__main__':
bar() bar()
""".format( """.format(
python=tmp_venv._bin("python") python=tmp_venv.python
) )
assert foo_script == tmp_venv._bin_dir.joinpath("foo").read_text() assert foo_script == tmp_venv._bin_dir.joinpath("foo").read_text()
...@@ -177,7 +177,7 @@ from fuz.foo import bar ...@@ -177,7 +177,7 @@ from fuz.foo import bar
if __name__ == '__main__': if __name__ == '__main__':
bar.baz() bar.baz()
""".format( """.format(
python=tmp_venv._bin("python") python=tmp_venv.python
) )
assert fox_script == tmp_venv._bin_dir.joinpath("fox").read_text() assert fox_script == tmp_venv._bin_dir.joinpath("fox").read_text()
......
...@@ -4,7 +4,7 @@ import subprocess ...@@ -4,7 +4,7 @@ import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Any
from typing import Union from typing import Union
import pytest import pytest
...@@ -118,9 +118,7 @@ def test_env_get_venv_with_venv_folder_present( ...@@ -118,9 +118,7 @@ def test_env_get_venv_with_venv_folder_present(
assert venv.path == in_project_venv_dir assert venv.path == in_project_venv_dir
def build_venv( def build_venv(path: Union[Path, str], **__: Any) -> ():
path: Union[Path, str], executable: Optional[str] = None, flags: bool = None
) -> ():
os.mkdir(str(path)) os.mkdir(str(path))
...@@ -161,6 +159,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( ...@@ -161,6 +159,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(
Path(tmp_dir) / "{}-py3.7".format(venv_name), Path(tmp_dir) / "{}-py3.7".format(venv_name),
executable="python3.7", executable="python3.7",
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") envs_file = TOMLFile(Path(tmp_dir) / "envs.toml")
...@@ -281,6 +282,9 @@ def test_activate_activates_different_virtualenv_with_envs_file( ...@@ -281,6 +282,9 @@ def test_activate_activates_different_virtualenv_with_envs_file(
Path(tmp_dir) / "{}-py3.6".format(venv_name), Path(tmp_dir) / "{}-py3.6".format(venv_name),
executable="python3.6", executable="python3.6",
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
assert envs_file.exists() assert envs_file.exists()
...@@ -335,6 +339,9 @@ def test_activate_activates_recreates_for_different_patch( ...@@ -335,6 +339,9 @@ def test_activate_activates_recreates_for_different_patch(
Path(tmp_dir) / "{}-py3.7".format(venv_name), Path(tmp_dir) / "{}-py3.7".format(venv_name),
executable="python3.7", executable="python3.7",
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
remove_venv_m.assert_called_with(Path(tmp_dir) / "{}-py3.7".format(venv_name)) remove_venv_m.assert_called_with(Path(tmp_dir) / "{}-py3.7".format(venv_name))
...@@ -715,6 +722,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ ...@@ -715,6 +722,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_
config_virtualenvs_path / "{}-py3.7".format(venv_name), config_virtualenvs_path / "{}-py3.7".format(venv_name),
executable="python3", executable="python3",
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
...@@ -739,6 +749,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific ...@@ -739,6 +749,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific
config_virtualenvs_path / "{}-py3.9".format(venv_name), config_virtualenvs_path / "{}-py3.9".format(venv_name),
executable="python3.9", executable="python3.9",
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
...@@ -823,6 +836,9 @@ def test_create_venv_uses_patch_version_to_detect_compatibility( ...@@ -823,6 +836,9 @@ def test_create_venv_uses_patch_version_to_detect_compatibility(
/ "{}-py{}.{}".format(venv_name, version.major, version.minor), / "{}-py{}.{}".format(venv_name, version.major, version.minor),
executable=None, executable=None,
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
...@@ -858,6 +874,9 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( ...@@ -858,6 +874,9 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable(
/ "{}-py{}.{}".format(venv_name, version.major, version.minor - 1), / "{}-py{}.{}".format(venv_name, version.major, version.minor - 1),
executable="python{}.{}".format(version.major, version.minor - 1), executable="python{}.{}".format(version.major, version.minor - 1),
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
...@@ -892,6 +911,9 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( ...@@ -892,6 +911,9 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(
poetry.file.parent / ".venv", poetry.file.parent / ".venv",
executable="python3.7", executable="python3.7",
flags={"always-copy": False, "system-site-packages": False}, flags={"always-copy": False, "system-site-packages": False},
with_pip=True,
with_setuptools=True,
with_wheel=True,
) )
envs_file = TOMLFile(Path(tmp_dir) / "virtualenvs" / "envs.toml") envs_file = TOMLFile(Path(tmp_dir) / "virtualenvs" / "envs.toml")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment