diff options
author | Bernát Gábor <bgabor8@bloomberg.net> | 2020-04-26 16:16:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-26 16:16:59 +0100 |
commit | dfafc1102a0746f3fe6efdee3520a8d83e88b9ef (patch) | |
tree | 8c04f955ea277a5fb06c340774cf18c87f59fb56 | |
parent | 078c945c861d53910481fb0c97d70f1d42cacfb9 (diff) | |
download | virtualenv-dfafc1102a0746f3fe6efdee3520a8d83e88b9ef.tar.gz |
Ensure makefile is present on CPython2 POSIX (#1787)
-rw-r--r-- | docs/changelog/1783.bugfix.rst | 1 | ||||
-rw-r--r-- | src/virtualenv/create/debug.py | 23 | ||||
-rw-r--r-- | src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py | 18 | ||||
-rw-r--r-- | src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py | 4 | ||||
-rw-r--r-- | src/virtualenv/create/via_global_ref/builtin/ref.py | 2 | ||||
-rw-r--r-- | src/virtualenv/discovery/py_info.py | 9 | ||||
-rw-r--r-- | tests/unit/create/test_creator.py | 27 |
7 files changed, 66 insertions, 18 deletions
diff --git a/docs/changelog/1783.bugfix.rst b/docs/changelog/1783.bugfix.rst new file mode 100644 index 0000000..47ebe0f --- /dev/null +++ b/docs/changelog/1783.bugfix.rst @@ -0,0 +1 @@ +On CPython2 POSIX platforms ensure ``syconfig.get_makefile_filename`` exists within the virtual environment (this is used by some c-extension based libraries - e.g. numpy - for building) - by :user:`gaborbernat`. diff --git a/src/virtualenv/create/debug.py b/src/virtualenv/create/debug.py index d015be6..23c380c 100644 --- a/src/virtualenv/create/debug.py +++ b/src/virtualenv/create/debug.py @@ -51,6 +51,14 @@ def run(): result["sys"]["fs_encoding"] = sys.getfilesystemencoding() result["sys"]["io_encoding"] = getattr(sys.stdout, "encoding", None) result["version"] = sys.version + + try: + import sysconfig + + result["makefile_filename"] = encode_path(sysconfig.get_makefile_filename()) + except ImportError: + pass + import os # landmark result["os"] = repr(os) @@ -84,11 +92,16 @@ def run(): import json result["json"] = repr(json) - print(json.dumps(result, indent=2)) - except (ImportError, ValueError, TypeError) as exception: # pragma: no cover - result["json"] = repr(exception) # pragma: no cover - print(repr(result)) # pragma: no cover - raise SystemExit(1) # pragma: no cover + except ImportError as exception: + result["json"] = repr(exception) + else: + try: + content = json.dumps(result, indent=2) + sys.stdout.write(content) + except (ValueError, TypeError) as exception: # pragma: no cover + sys.stderr.write(repr(exception)) + sys.stdout.write(repr(result)) # pragma: no cover + raise SystemExit(1) # pragma: no cover if __name__ == "__main__": diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py index 61aa395..555b0c5 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py @@ -54,7 +54,23 @@ class CPython2(CPython, Python2): return dirs -class CPython2Posix(CPython2, CPythonPosix): +@add_metaclass(abc.ABCMeta) +class CPython2PosixBase(CPython2, CPythonPosix): + """common to macOs framework builds and other posix CPython2""" + + @classmethod + def sources(cls, interpreter): + for src in super(CPython2PosixBase, cls).sources(interpreter): + yield src + + # check if the makefile exists and if so make it available under the virtual environment + make_file = Path(interpreter.sysconfig["makefile_filename"]) + if make_file.exists() and str(make_file).startswith(interpreter.prefix): + under_prefix = make_file.relative_to(Path(interpreter.prefix)) + yield PathRefToDest(make_file, dest=lambda self, s: self.dest / under_prefix) + + +class CPython2Posix(CPython2PosixBase): """CPython 2 on POSIX (excluding macOs framework builds)""" @classmethod diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py b/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py index b7ffaf1..7967a81 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py @@ -14,7 +14,7 @@ from virtualenv.util.path import Path from virtualenv.util.six import ensure_text from .common import CPython, CPythonPosix, is_mac_os_framework -from .cpython2 import CPython2 +from .cpython2 import CPython2PosixBase from .cpython3 import CPython3 @@ -65,7 +65,7 @@ class CPythonmacOsFramework(CPython): raise NotImplementedError -class CPython2macOsFramework(CPythonmacOsFramework, CPython2, CPythonPosix): +class CPython2macOsFramework(CPythonmacOsFramework, CPython2PosixBase): @classmethod def image_ref(cls, interpreter): return Path(interpreter.prefix) / "Python" diff --git a/src/virtualenv/create/via_global_ref/builtin/ref.py b/src/virtualenv/create/via_global_ref/builtin/ref.py index 9022994..263da3b 100644 --- a/src/virtualenv/create/via_global_ref/builtin/ref.py +++ b/src/virtualenv/create/via_global_ref/builtin/ref.py @@ -122,6 +122,8 @@ class PathRefToDest(PathRef): dest = self.dest(creator, self.src) method = self.method(symlinks) dest_iterable = dest if isinstance(dest, list) else (dest,) + if not dest.parent.exists(): + dest.parent.mkdir(parents=True, exist_ok=True) for dst in dest_iterable: method(self.src, dst) diff --git a/src/virtualenv/discovery/py_info.py b/src/virtualenv/discovery/py_info.py index 6d76cbe..516faf1 100644 --- a/src/virtualenv/discovery/py_info.py +++ b/src/virtualenv/discovery/py_info.py @@ -75,6 +75,15 @@ class PythonInfo(object): self.stdout_encoding = u(getattr(sys.stdout, "encoding", None)) self.sysconfig_paths = {u(i): u(sysconfig.get_path(i, expand=False)) for i in sysconfig.get_path_names()} + + self.sysconfig = { + u(k): u(v) + for k, v in [ # a list of content to store from sysconfig + ("makefile_filename", sysconfig.get_makefile_filename()), + ] + if k is not None + } + config_var_keys = set() for element in self.sysconfig_paths.values(): for k in _CONF_VAR_RE.findall(element): diff --git a/tests/unit/create/test_creator.py b/tests/unit/create/test_creator.py index 39fcbbe..3174d45 100644 --- a/tests/unit/create/test_creator.py +++ b/tests/unit/create/test_creator.py @@ -20,6 +20,7 @@ import pytest from virtualenv.__main__ import run, run_with_catch from virtualenv.create.creator import DEBUG_SCRIPT, Creator, get_env_debug_info +from virtualenv.create.via_global_ref.builtin.cpython.cpython2 import CPython2PosixBase from virtualenv.create.via_global_ref.builtin.cpython.cpython3 import CPython3Posix from virtualenv.create.via_global_ref.builtin.python2.python2 import Python2 from virtualenv.discovery.builtin import get_interpreter @@ -145,18 +146,20 @@ def test_create_no_seed(python, creator, isolated, system, coverage_env, special if isolated == "global": cmd.append("--system-site-packages") result = cli_run(cmd) + creator = result.creator coverage_env() if IS_PYPY: # pypy cleans up file descriptors periodically so our (many) subprocess calls impact file descriptor limits # force a close of these on system where the limit is low-ish (e.g. MacOS 256) gc.collect() - purelib = result.creator.purelib + purelib = creator.purelib patch_files = {purelib / "{}.{}".format("_virtualenv", i) for i in ("py", "pyc", "pth")} patch_files.add(purelib / "__pycache__") - content = set(result.creator.purelib.iterdir()) - patch_files + content = set(creator.purelib.iterdir()) - patch_files assert not content, "\n".join(ensure_text(str(i)) for i in content) - assert result.creator.env_name == ensure_text(dest.name) - debug = result.creator.debug + assert creator.env_name == ensure_text(dest.name) + debug = creator.debug + assert "exception" not in debug, "{}\n{}\n{}".format(debug.get("exception"), debug.get("out"), debug.get("err")) sys_path = cleanup_sys_path(debug["sys"]["path"]) system_sys_path = cleanup_sys_path(system["sys"]["path"]) our_paths = set(sys_path) - set(system_sys_path) @@ -205,28 +208,32 @@ def test_create_no_seed(python, creator, isolated, system, coverage_env, special # for venv some repackaging does not includes the pythonx.y exes = exes[:-1] for exe in exes: - exe_path = result.creator.bin_dir / exe - assert exe_path.exists(), "\n".join(str(i) for i in result.creator.bin_dir.iterdir()) + exe_path = creator.bin_dir / exe + assert exe_path.exists(), "\n".join(str(i) for i in creator.bin_dir.iterdir()) if not exe_path.is_symlink(): # option 1: a real file continue # it was a file link = os.readlink(str(exe_path)) if not os.path.isabs(link): # option 2: a relative symlink continue # option 3: an absolute symlink, should point outside the venv - assert not link.startswith(str(result.creator.dest)) + assert not link.startswith(str(creator.dest)) if IS_WIN and CURRENT.implementation == "CPython": - python_w = result.creator.exe.parent / "pythonw.exe" + python_w = creator.exe.parent / "pythonw.exe" assert python_w.exists() - assert python_w.read_bytes() != result.creator.exe.read_bytes() + assert python_w.read_bytes() != creator.exe.read_bytes() if CPython3Posix.pyvenv_launch_patch_active(PythonInfo.from_exe(python)) and creator_key != "venv": result = subprocess.check_output( - [str(result.creator.exe), "-c", 'import os; print(os.environ.get("__PYVENV_LAUNCHER__"))'], + [str(creator.exe), "-c", 'import os; print(os.environ.get("__PYVENV_LAUNCHER__"))'], universal_newlines=True, ).strip() assert result == "None" + if isinstance(creator, CPython2PosixBase): + make_file = debug["makefile_filename"] + assert os.path.exists(make_file) + @pytest.mark.skipif(not CURRENT.has_venv, reason="requires interpreter with venv") def test_venv_fails_not_inline(tmp_path, capsys, mocker): |