summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernát Gábor <bgabor8@bloomberg.net>2020-04-26 16:16:59 +0100
committerGitHub <noreply@github.com>2020-04-26 16:16:59 +0100
commitdfafc1102a0746f3fe6efdee3520a8d83e88b9ef (patch)
tree8c04f955ea277a5fb06c340774cf18c87f59fb56
parent078c945c861d53910481fb0c97d70f1d42cacfb9 (diff)
downloadvirtualenv-dfafc1102a0746f3fe6efdee3520a8d83e88b9ef.tar.gz
Ensure makefile is present on CPython2 POSIX (#1787)
-rw-r--r--docs/changelog/1783.bugfix.rst1
-rw-r--r--src/virtualenv/create/debug.py23
-rw-r--r--src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py18
-rw-r--r--src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py4
-rw-r--r--src/virtualenv/create/via_global_ref/builtin/ref.py2
-rw-r--r--src/virtualenv/discovery/py_info.py9
-rw-r--r--tests/unit/create/test_creator.py27
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):