diff --git a/.coveragerc b/.coveragerc index 2d64afd..a1dd90c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -18,7 +18,8 @@ source = src .tox/*/lib/python*/site-packages .tox/pypy*/site-packages - .tox\*\Lib\site-packages\ + .tox\*\Lib\site-packages + .tox\py\site-packages */src *\src diff --git a/src/virtualenv/create/creator.py b/src/virtualenv/create/creator.py index 6363f8b..f7b8554 100644 --- a/src/virtualenv/create/creator.py +++ b/src/virtualenv/create/creator.py @@ -227,7 +227,13 @@ def get_env_debug_info(env_exe, debug_script, app_data, env): # noinspection PyBroadException try: if code != 0: - result = literal_eval(out) + if out: + result = literal_eval(out) + else: + if code == 2 and "file" in err: + # Re-raise FileNotFoundError from `run_cmd()` + raise OSError(err) + raise Exception(err) else: result = json.loads(out) if err: diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/common.py b/src/virtualenv/create/via_global_ref/builtin/pypy/common.py index b0cd401..4504d81 100644 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/common.py +++ b/src/virtualenv/create/via_global_ref/builtin/pypy/common.py @@ -23,6 +23,11 @@ class PyPy(ViaGlobalRefVirtualenvBuiltin): must = RefMust.COPY if interpreter.version_info.major == 2 else RefMust.NA yield host, targets, must, RefWhen.ANY + @classmethod + def executables(cls, interpreter): + for src in super(PyPy, cls).sources(interpreter): + yield src + @classmethod def exe_names(cls, interpreter): return { @@ -34,8 +39,8 @@ class PyPy(ViaGlobalRefVirtualenvBuiltin): @classmethod def sources(cls, interpreter): - for src in super(PyPy, cls).sources(interpreter): - yield src + for exe in cls.executables(interpreter): + yield exe for host in cls._add_shared_libs(interpreter): yield PathRefToDest(host, dest=lambda self, s: self.bin_dir / s.name) diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py index cc72c14..a01b70f 100644 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py +++ b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py @@ -66,9 +66,15 @@ class PyPy3Posix(PyPy3, PosixSupports): class Pypy3Windows(PyPy3, WindowsSupports): """PyPy 3 on Windows""" + @property + def less_v37(self): + return self.interpreter.version_info.minor < 7 + @property def stdlib(self): """PyPy3 respects sysconfig only for the host python, virtual envs is instead Lib/site-packages""" + if self.less_v37: + return self.dest / "site-packages" return self.dest / "Lib" / "site-packages" @property diff --git a/src/virtualenv/create/via_global_ref/venv.py b/src/virtualenv/create/via_global_ref/venv.py index aaa6794..d9fe9eb 100644 --- a/src/virtualenv/create/via_global_ref/venv.py +++ b/src/virtualenv/create/via_global_ref/venv.py @@ -10,6 +10,7 @@ from virtualenv.util.path import ensure_dir from virtualenv.util.subprocess import run_cmd from .api import ViaGlobalRefApi, ViaGlobalRefMeta +from .builtin.pypy.pypy3 import Pypy3Windows class Venv(ViaGlobalRefApi): @@ -41,6 +42,18 @@ class Venv(ViaGlobalRefApi): for lib in self.libs: ensure_dir(lib) super(Venv, self).create() + self.executables_for_win_pypy_less_v37() + + def executables_for_win_pypy_less_v37(self): + """ + PyPy <= 3.6 (v7.3.3) for Windows contains only pypy3.exe and pypy3w.exe + Venv does not handle non-existing exe sources, e.g. python.exe, so this + patch does it. + """ + creator = self.describe + if isinstance(creator, Pypy3Windows) and creator.less_v37: + for exe in creator.executables(self.interpreter): + exe.run(creator, self.symlinks) def create_inline(self): from venv import EnvBuilder diff --git a/src/virtualenv/util/subprocess/__init__.py b/src/virtualenv/util/subprocess/__init__.py index f506626..9f6ffd7 100644 --- a/src/virtualenv/util/subprocess/__init__.py +++ b/src/virtualenv/util/subprocess/__init__.py @@ -27,8 +27,11 @@ def run_cmd(cmd): ) out, err = process.communicate() # input disabled code = process.returncode - except OSError as os_error: - code, out, err = os_error.errno, "", os_error.strerror + except OSError as error: + code, out, err = error.errno, "", error.strerror + if code == 2 and "file" in err: + # FileNotFoundError in Python >= 3.3 + err = str(error) return code, out, err