From 43a5e0383b33867b73eb1d6f51f9f0a0c063137e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Mon, 12 Oct 2020 12:53:13 +0100 Subject: [PATCH] Always copy executable on Windows 3+ (#1977) --- .../via_global_ref/builtin/cpython/common.py | 19 ++++++------ .../builtin/cpython/cpython3.py | 29 +++++++------------ .../builtin/via_global_self_do.py | 9 ++++-- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/common.py b/src/virtualenv/create/via_global_ref/builtin/cpython/common.py index 39c56cb3e..c93f9f31e 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/common.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/common.py @@ -42,18 +42,19 @@ def _executables(cls, interpreter): class CPythonWindows(CPython, WindowsSupports): @classmethod def _executables(cls, interpreter): - executables = cls._win_executables(Path(interpreter.system_executable), interpreter, RefWhen.ANY) - for src, targets, must, when in executables: - yield src, targets, must, when - - @classmethod - def _win_executables(cls, host, interpreter, when): - must = RefMust.COPY if interpreter.version_info.major == 2 else RefMust.NA + # symlink of the python executables does not work reliably, copy always instead + # - https://bugs.python.org/issue42013 + # - venv + host = cls.host_python(interpreter) for path in (host.parent / n for n in {"python.exe", host.name}): - yield host, [path.name], must, when + yield host, [path.name], RefMust.COPY, RefWhen.ANY # for more info on pythonw.exe see https://stackoverflow.com/a/30313091 python_w = host.parent / "pythonw.exe" - yield python_w, [python_w.name], must, when + yield python_w, [python_w.name], RefMust.COPY, RefWhen.ANY + + @classmethod + def host_python(cls, interpreter): + return Path(interpreter.system_executable) def is_mac_os_framework(interpreter): diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py index 149e8edd4..19385095f 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py @@ -1,13 +1,12 @@ from __future__ import absolute_import, unicode_literals import abc -from itertools import chain from textwrap import dedent from six import add_metaclass from virtualenv.create.describe import Python3Supports -from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest, RefMust, RefWhen +from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest from virtualenv.create.via_global_ref.store import is_store_python from virtualenv.util.path import Path @@ -56,29 +55,21 @@ def setup_meta(cls, interpreter): def sources(cls, interpreter): for src in super(CPython3Windows, cls).sources(interpreter): yield src - if cls.venv_37p(interpreter): - for dll in (i for i in Path(interpreter.system_executable).parent.iterdir() if i.suffix == ".dll"): - yield PathRefToDest(dll, cls.to_bin, RefMust.SYMLINK, RefWhen.SYMLINK) - else: + if not cls.venv_37p(interpreter): for src in cls.include_dll_and_pyd(interpreter): yield src + @staticmethod + def venv_37p(interpreter): + return interpreter.version_info.minor >= 7 + @classmethod - def _executables(cls, interpreter): - system_exe = Path(interpreter.system_executable) + def host_python(cls, interpreter): if cls.venv_37p(interpreter): # starting with CPython 3.7 Windows ships with a venvlauncher.exe that avoids the need for dll/pyd copies - launcher = Path(interpreter.system_stdlib) / "venv" / "scripts" / "nt" / "python.exe" - executables = cls._win_executables(launcher, interpreter, RefWhen.COPY) - executables = chain(executables, cls._win_executables(system_exe, interpreter, RefWhen.SYMLINK)) - else: - executables = cls._win_executables(system_exe, interpreter, RefWhen.ANY) - for src, targets, must, when in executables: - yield src, targets, must, when - - @staticmethod - def venv_37p(interpreter): - return interpreter.version_info.minor > 6 + # it also means the wrapper must be copied to avoid bugs such as https://bugs.python.org/issue42013 + return Path(interpreter.system_stdlib) / "venv" / "scripts" / "nt" / "python.exe" + return super(CPython3Windows, cls).host_python(interpreter) @classmethod def include_dll_and_pyd(cls, interpreter): diff --git a/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py b/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py index a00b97aae..863ae16e1 100644 --- a/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py +++ b/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py @@ -4,7 +4,7 @@ from six import add_metaclass -from virtualenv.create.via_global_ref.builtin.ref import ExePathRefToDest, RefMust +from virtualenv.create.via_global_ref.builtin.ref import ExePathRefToDest, RefMust, RefWhen from virtualenv.util.path import ensure_dir from ..api import ViaGlobalRefApi, ViaGlobalRefMeta @@ -89,7 +89,12 @@ def create(self): try: self.enable_system_site_package = False for src in self._sources: - src.run(self, self.symlinks) + if ( + src.when == RefWhen.ANY + or (src.when == RefWhen.SYMLINK and self.symlinks is True) + or (src.when == RefWhen.COPY and self.symlinks is False) + ): + src.run(self, self.symlinks) finally: if true_system_site != self.enable_system_site_package: self.enable_system_site_package = true_system_site