Commit cb34c3d0 by finswimmer Committed by GitHub

config: support virtualenv `--always-copy` option

Mitigates: #3134
parent 9c8155c6
......@@ -34,6 +34,7 @@ which will give you something similar to this:
cache-dir = "/path/to/cache/directory"
virtualenvs.create = true
virtualenvs.in-project = null
virtualenvs.options.always-copy = true
virtualenvs.path = "{cache-dir}/virtualenvs" # /path/to/cache/directory/virtualenvs
```
......@@ -128,6 +129,12 @@ If not set explicitly (default), `poetry` will use the virtualenv from the `.ven
Directory where virtual environments will be created.
Defaults to `{cache-dir}/virtualenvs` (`{cache-dir}\virtualenvs` on Windows).
### `virtualenvs.options.always-copy`: boolean
If set to `true` the `--always-copy` parameter is passed to `virtualenv` on creation of the venv. Thus all needed files are copied into the venv instead of symlinked.
Defaults to `false`.
### `repositories.<name>`: string
Set a new alternative repository. See [Repositories](/docs/repositories/) for more information.
......@@ -36,6 +36,7 @@ class Config(object):
"create": True,
"in-project": None,
"path": os.path.join("{cache-dir}", "virtualenvs"),
"options": {"always-copy": False},
},
"experimental": {"new-installer": True},
}
......@@ -87,7 +88,11 @@ class Config(object):
for key in config:
value = self.get(parent_key + key)
if isinstance(value, dict):
all_[key] = _all(config[key], parent_key=key + ".")
if parent_key != "":
current_parent = parent_key + key + "."
else:
current_parent = key + "."
all_[key] = _all(config[key], parent_key=current_parent)
continue
all_[key] = value
......@@ -131,14 +136,22 @@ class Config(object):
return re.sub(r"{(.+?)}", lambda m: self.get(m.group(1)), value)
def _get_validator(self, name): # type: (str) -> Callable
if name in {"virtualenvs.create", "virtualenvs.in-project"}:
if name in {
"virtualenvs.create",
"virtualenvs.in-project",
"virtualenvs.options.always-copy",
}:
return boolean_validator
if name == "virtualenvs.path":
return str
def _get_normalizer(self, name): # type: (str) -> Callable
if name in {"virtualenvs.create", "virtualenvs.in-project"}:
if name in {
"virtualenvs.create",
"virtualenvs.in-project",
"virtualenvs.options.always-copy",
}:
return boolean_normalizer
if name == "virtualenvs.path":
......
......@@ -64,6 +64,11 @@ To remove a repository (repo is a short alias for repositories):
boolean_normalizer,
True,
),
"virtualenvs.options.always-copy": (
boolean_validator,
boolean_normalizer,
False,
),
}
return unique_config_values
......
......@@ -642,7 +642,11 @@ class EnvManager(object):
"Creating virtualenv <c1>{}</> in {}".format(name, str(venv_path))
)
self.build_venv(venv, executable=executable)
self.build_venv(
venv,
executable=executable,
flags=self._poetry.config.get("virtualenvs.options"),
)
else:
if force:
if not env.is_sane():
......@@ -655,7 +659,11 @@ class EnvManager(object):
"Recreating virtualenv <c1>{}</> in {}".format(name, str(venv))
)
self.remove_venv(venv)
self.build_venv(venv, executable=executable)
self.build_venv(
venv,
executable=executable,
flags=self._poetry.config.get("virtualenvs.options"),
)
elif io.is_very_verbose():
io.write_line("Virtualenv <c1>{}</> already exists.".format(name))
......@@ -679,19 +687,26 @@ class EnvManager(object):
@classmethod
def build_venv(
cls, path, executable=None
): # type: (Union[Path,str], Optional[Union[str, Path]]) -> virtualenv.run.session.Session
cls, path, executable=None, flags=None
): # type: (Union[Path,str], Optional[Union[str, Path]], Dict[str, bool]) -> virtualenv.run.session.Session
flags = flags or {}
if isinstance(executable, Path):
executable = executable.resolve().as_posix()
return virtualenv.cli_run(
[
"--no-download",
"--no-periodic-update",
"--python",
executable or sys.executable,
str(path),
]
)
args = [
"--no-download",
"--no-periodic-update",
"--python",
executable or sys.executable,
str(path),
]
for flag, value in flags.items():
if value is True:
args.insert(0, "--{}".format(flag))
return virtualenv.cli_run(args)
@classmethod
def remove_venv(cls, path): # type: (Union[Path,str]) -> None
......
......@@ -5,7 +5,9 @@ from poetry.core.semver import Version
from poetry.utils._compat import Path
def build_venv(path, executable=None): # type: (Union[Path,str], Optional[str]) -> ()
def build_venv(
path, executable=None, flags=None
): # type: (Union[Path,str], Optional[str], bool) -> ()
Path(path).mkdir(parents=True, exist_ok=True)
......
......@@ -50,7 +50,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(
tester.execute("3.7")
venv_py37 = venv_cache / "{}-py3.7".format(venv_name)
mock_build_env.assert_called_with(venv_py37, executable="python3.7")
mock_build_env.assert_called_with(
venv_py37, executable="python3.7", flags={"always-copy": False}
)
envs_file = TOMLFile(venv_cache / "envs.toml")
assert envs_file.exists()
......
......@@ -30,6 +30,7 @@ def test_list_displays_default_value_if_not_set(tester, config):
experimental.new-installer = true
virtualenvs.create = true
virtualenvs.in-project = null
virtualenvs.options.always-copy = false
virtualenvs.path = {path} # /foo{sep}virtualenvs
""".format(
path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep
......@@ -47,6 +48,7 @@ def test_list_displays_set_get_setting(tester, config):
experimental.new-installer = true
virtualenvs.create = false
virtualenvs.in-project = null
virtualenvs.options.always-copy = false
virtualenvs.path = {path} # /foo{sep}virtualenvs
""".format(
path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep
......@@ -86,6 +88,7 @@ def test_list_displays_set_get_local_setting(tester, config):
experimental.new-installer = true
virtualenvs.create = false
virtualenvs.in-project = null
virtualenvs.options.always-copy = false
virtualenvs.path = {path} # /foo{sep}virtualenvs
""".format(
path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep
......
......@@ -118,7 +118,9 @@ def test_env_get_venv_with_venv_folder_present(
assert venv.path == in_project_venv_dir
def build_venv(path, executable=None): # type: (Union[Path,str], Optional[str]) -> ()
def build_venv(
path, executable=None, flags=None
): # type: (Union[Path,str], Optional[str], bool) -> ()
os.mkdir(str(path))
......@@ -156,7 +158,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(
venv_name = EnvManager.generate_env_name("simple-project", str(poetry.file.parent))
m.assert_called_with(
Path(tmp_dir) / "{}-py3.7".format(venv_name), executable="python3.7"
Path(tmp_dir) / "{}-py3.7".format(venv_name),
executable="python3.7",
flags={"always-copy": False},
)
envs_file = TOMLFile(Path(tmp_dir) / "envs.toml")
......@@ -274,7 +278,9 @@ def test_activate_activates_different_virtualenv_with_envs_file(
env = manager.activate("python3.6", NullIO())
m.assert_called_with(
Path(tmp_dir) / "{}-py3.6".format(venv_name), executable="python3.6"
Path(tmp_dir) / "{}-py3.6".format(venv_name),
executable="python3.6",
flags={"always-copy": False},
)
assert envs_file.exists()
......@@ -326,7 +332,9 @@ def test_activate_activates_recreates_for_different_patch(
env = manager.activate("python3.7", NullIO())
build_venv_m.assert_called_with(
Path(tmp_dir) / "{}-py3.7".format(venv_name), executable="python3.7"
Path(tmp_dir) / "{}-py3.7".format(venv_name),
executable="python3.7",
flags={"always-copy": False},
)
remove_venv_m.assert_called_with(Path(tmp_dir) / "{}-py3.7".format(venv_name))
......@@ -654,7 +662,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_
manager.create_venv(NullIO())
m.assert_called_with(
Path("/foo/virtualenvs/{}-py3.7".format(venv_name)), executable="python3"
Path("/foo/virtualenvs/{}-py3.7".format(venv_name)),
executable="python3",
flags={"always-copy": False},
)
......@@ -678,7 +688,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific
manager.create_venv(NullIO())
m.assert_called_with(
Path("/foo/virtualenvs/{}-py3.9".format(venv_name)), executable="python3.9"
Path("/foo/virtualenvs/{}-py3.9".format(venv_name)),
executable="python3.9",
flags={"always-copy": False},
)
......@@ -767,6 +779,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility(
)
),
executable=None,
flags={"always-copy": False},
)
......@@ -804,6 +817,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable(
)
),
executable="python{}.{}".format(version.major, version.minor - 1),
flags={"always-copy": False},
)
......@@ -834,7 +848,11 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(
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="python3.7",
flags={"always-copy": False},
)
envs_file = TOMLFile(Path(tmp_dir) / "virtualenvs" / "envs.toml")
assert not envs_file.exists()
......
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