summaryrefslogtreecommitdiff
path: root/src/virtualenv
diff options
context:
space:
mode:
authorBernát Gábor <bgabor8@bloomberg.net>2020-01-18 17:07:49 +0000
committerGitHub <noreply@github.com>2020-01-18 17:07:49 +0000
commit8d6af57d76edcf425beab6d53d4c14f1e49f7ca5 (patch)
treede400611862464320c6a45a932e27c2bac0c63ea /src/virtualenv
parent8f4128879b26921af2ec0f0f3f690ed2324bfb04 (diff)
downloadvirtualenv-8d6af57d76edcf425beab6d53d4c14f1e49f7ca5.tar.gz
CentOs and Fedora support (#1500)
* CentOs support Instead of hard coding patterns and guessing let's ask the host python via the sysconfig. Signed-off-by: Bernat Gabor <bgabor8@bloomberg.net> * add isolated test Signed-off-by: Bernat Gabor <bgabor8@bloomberg.net> * fixes for Fedora Signed-off-by: Bernat Gabor <bgabor8@bloomberg.net>
Diffstat (limited to 'src/virtualenv')
-rw-r--r--src/virtualenv/activation/python/__init__.py2
-rw-r--r--src/virtualenv/activation/via_template.py2
-rw-r--r--src/virtualenv/info.py2
-rw-r--r--src/virtualenv/interpreters/create/builtin_way.py16
-rw-r--r--src/virtualenv/interpreters/create/cpython/common.py24
-rw-r--r--src/virtualenv/interpreters/create/creator.py50
-rw-r--r--src/virtualenv/interpreters/create/debug.py8
-rw-r--r--src/virtualenv/interpreters/create/pypy/common.py4
-rw-r--r--src/virtualenv/interpreters/create/pypy/pypy2.py25
-rw-r--r--src/virtualenv/interpreters/create/pypy/pypy3.py43
-rw-r--r--src/virtualenv/interpreters/create/venv.py38
-rw-r--r--src/virtualenv/interpreters/create/via_global_ref/python2.py14
-rw-r--r--src/virtualenv/interpreters/create/via_global_ref/via_global_self_do.py22
-rw-r--r--src/virtualenv/interpreters/discovery/py_info.py84
-rw-r--r--src/virtualenv/pyenv_cfg.py3
-rw-r--r--src/virtualenv/seed/via_app_data/pip_install/base.py9
-rw-r--r--src/virtualenv/util/path/_permission.py2
-rw-r--r--src/virtualenv/util/path/_sync.py4
-rw-r--r--src/virtualenv/util/subprocess/_win_subprocess.py1
19 files changed, 172 insertions, 181 deletions
diff --git a/src/virtualenv/activation/python/__init__.py b/src/virtualenv/activation/python/__init__.py
index b06af78..8d7e80e 100644
--- a/src/virtualenv/activation/python/__init__.py
+++ b/src/virtualenv/activation/python/__init__.py
@@ -14,6 +14,6 @@ class PythonActivator(ViaTemplateActivator):
def replacements(self, creator, dest_folder):
replacements = super(PythonActivator, self).replacements(creator, dest_folder)
- site_dump = json.dumps([os.path.relpath(str(i), str(dest_folder)) for i in creator.site_packages], indent=2)
+ site_dump = json.dumps(list({os.path.relpath(str(i), str(dest_folder)) for i in creator.libs}), indent=2)
replacements.update({"__SITE_PACKAGES__": site_dump})
return replacements
diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py
index f707897..442776a 100644
--- a/src/virtualenv/activation/via_template.py
+++ b/src/virtualenv/activation/via_template.py
@@ -31,7 +31,7 @@ class ViaTemplateActivator(Activator):
"__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt,
"__VIRTUAL_ENV__": six.ensure_text(str(creator.dest_dir)),
"__VIRTUAL_NAME__": creator.env_name,
- "__BIN_NAME__": six.ensure_text(str(creator.bin_name)),
+ "__BIN_NAME__": six.ensure_text(str(creator.bin_dir.relative_to(creator.dest_dir))),
"__PATH_SEP__": os.pathsep,
}
diff --git a/src/virtualenv/info.py b/src/virtualenv/info.py
index 573a97f..fbb7573 100644
--- a/src/virtualenv/info.py
+++ b/src/virtualenv/info.py
@@ -45,7 +45,7 @@ def is_fs_case_sensitive():
with tempfile.NamedTemporaryFile(prefix="TmP") as tmp_file:
_FS_CASE_SENSITIVE = not os.path.exists(tmp_file.name.lower())
logging.debug(
- "filesystem under is %r%s case-sensitive", tmp_file.name, "" if _FS_CASE_SENSITIVE else " not"
+ "filesystem under %r is %scase-sensitive", tmp_file.name, "" if _FS_CASE_SENSITIVE else "not "
)
return _FS_CASE_SENSITIVE
diff --git a/src/virtualenv/interpreters/create/builtin_way.py b/src/virtualenv/interpreters/create/builtin_way.py
index 4134d7b..31ea9e4 100644
--- a/src/virtualenv/interpreters/create/builtin_way.py
+++ b/src/virtualenv/interpreters/create/builtin_way.py
@@ -13,22 +13,6 @@ class VirtualenvBuiltin(ViaGlobalRefApi):
"""A creator that does operations itself without delegation"""
@property
- def bin_name(self):
- raise NotImplementedError
-
- @property
- def bin_dir(self):
- return self.dest_dir / self.bin_name
-
- @property
- def lib_dir(self):
- raise NotImplementedError
-
- @property
- def site_packages(self):
- return [self.lib_dir / "site-packages"]
-
- @property
def exe_name(self):
raise NotImplementedError
diff --git a/src/virtualenv/interpreters/create/cpython/common.py b/src/virtualenv/interpreters/create/cpython/common.py
index 490b0ad..ba921b1 100644
--- a/src/virtualenv/interpreters/create/cpython/common.py
+++ b/src/virtualenv/interpreters/create/cpython/common.py
@@ -24,18 +24,6 @@ class CPython(ViaGlobalRefVirtualenvBuiltin):
class CPythonPosix(CPython, PosixSupports):
"""Create a CPython virtual environment on POSIX platforms"""
- @property
- def bin_name(self):
- return "bin"
-
- @property
- def lib_name(self):
- return "lib"
-
- @property
- def lib_base(self):
- return Path(self.lib_name) / self.interpreter.python_name
-
def link_exe(self):
host = Path(self.interpreter.system_executable)
major, minor = self.interpreter.version_info.major, self.interpreter.version_info.minor
@@ -44,18 +32,6 @@ class CPythonPosix(CPython, PosixSupports):
@six.add_metaclass(abc.ABCMeta)
class CPythonWindows(CPython, WindowsSupports):
- @property
- def bin_name(self):
- return "Scripts"
-
- @property
- def lib_name(self):
- return "Lib"
-
- @property
- def lib_base(self):
- return Path(self.lib_name)
-
def link_exe(self):
host = Path(self.interpreter.system_executable)
return {p: [p.name] for p in (host.parent / n for n in ("python.exe", "pythonw.exe", host.name)) if p.exists()}
diff --git a/src/virtualenv/interpreters/create/creator.py b/src/virtualenv/interpreters/create/creator.py
index 1185b56..f0adf9e 100644
--- a/src/virtualenv/interpreters/create/creator.py
+++ b/src/virtualenv/interpreters/create/creator.py
@@ -36,6 +36,55 @@ class Creator(object):
self.clear = options.clear
self.pyenv_cfg = PyEnvCfg.from_folder(self.dest_dir)
+ self._stdlib = None
+ self._system_stdlib = None
+ self._conf_vars = None
+
+ @property
+ def bin_dir(self):
+ return self.script_dir
+
+ @property
+ def script_dir(self):
+ return self.dest_dir / Path(self.interpreter.distutils_install["scripts"])
+
+ @property
+ def purelib(self):
+ return self.dest_dir / self.interpreter.distutils_install["purelib"]
+
+ @property
+ def platlib(self):
+ return self.dest_dir / self.interpreter.distutils_install["platlib"]
+
+ @property
+ def libs(self):
+ return list(OrderedDict(((self.platlib, None), (self.purelib, None))).keys())
+
+ @property
+ def stdlib(self):
+ if self._stdlib is None:
+ self._stdlib = Path(self.interpreter.sysconfig_path("stdlib", config_var=self._config_vars))
+ return self._stdlib
+
+ @property
+ def system_stdlib(self):
+ if self._system_stdlib is None:
+ conf_vars = self._calc_config_vars(self.interpreter.system_prefix)
+ self._system_stdlib = Path(self.interpreter.sysconfig_path("stdlib", conf_vars))
+ return self._system_stdlib
+
+ @property
+ def _config_vars(self):
+ if self._conf_vars is None:
+ self._conf_vars = self._calc_config_vars(six.ensure_text(str(self.dest_dir)))
+ return self._conf_vars
+
+ def _calc_config_vars(self, to):
+ return {
+ k: (to if v.startswith(self.interpreter.prefix) else v)
+ for k, v in self.interpreter.sysconfig_config_vars.items()
+ }
+
def __repr__(self):
return six.ensure_str(self.__unicode__())
@@ -154,6 +203,7 @@ class Creator(object):
"home": self.interpreter.system_exec_prefix,
"include-system-site-packages": "true" if self.enable_system_site_package else "false",
"implementation": self.interpreter.implementation,
+ "version_info": ".".join(str(i) for i in self.interpreter.version_info),
"virtualenv": __version__,
}
diff --git a/src/virtualenv/interpreters/create/debug.py b/src/virtualenv/interpreters/create/debug.py
index e2b1585..674d1bb 100644
--- a/src/virtualenv/interpreters/create/debug.py
+++ b/src/virtualenv/interpreters/create/debug.py
@@ -66,6 +66,14 @@ def run():
except ImportError as exception: # pragma: no cover
result["datetime"] = repr(exception) # pragma: no cover
+ try:
+ # noinspection PyUnresolvedReferences
+ import math # site
+
+ result["math"] = repr(math)
+ except ImportError as exception: # pragma: no cover
+ result["math"] = repr(exception) # pragma: no cover
+
# try to print out, this will validate if other core modules are available (json in this case)
try:
import json
diff --git a/src/virtualenv/interpreters/create/pypy/common.py b/src/virtualenv/interpreters/create/pypy/common.py
index 1c98064..72dfb21 100644
--- a/src/virtualenv/interpreters/create/pypy/common.py
+++ b/src/virtualenv/interpreters/create/pypy/common.py
@@ -14,10 +14,6 @@ class PyPy(ViaGlobalRefVirtualenvBuiltin):
def supports(cls, interpreter):
return interpreter.implementation == "PyPy" and super(PyPy, cls).supports(interpreter)
- @property
- def site_packages(self):
- return [self.dest_dir / "site-packages"]
-
def link_exe(self):
host = Path(self.interpreter.system_executable)
return {host: sorted("{}{}".format(name, self.suffix) for name in self.exe_names())}
diff --git a/src/virtualenv/interpreters/create/pypy/pypy2.py b/src/virtualenv/interpreters/create/pypy/pypy2.py
index fe34cdc..b16b53d 100644
--- a/src/virtualenv/interpreters/create/pypy/pypy2.py
+++ b/src/virtualenv/interpreters/create/pypy/pypy2.py
@@ -6,7 +6,6 @@ import six
from virtualenv.interpreters.create.support import PosixSupports, WindowsSupports
from virtualenv.interpreters.create.via_global_ref.python2 import Python2
-from virtualenv.util.path import Path
from .common import PyPy
@@ -20,21 +19,17 @@ class PyPy2(PyPy, Python2):
return "pypy"
@property
- def lib_name(self):
- return "lib-python"
-
- @property
def lib_pypy(self):
return self.dest_dir / "lib_pypy"
- @property
- def lib_base(self):
- return Path(self.lib_name) / self.interpreter.version_release_str
+ def _calc_config_vars(self, to):
+ base = super(PyPy, self)._calc_config_vars(to)
+ # for some reason pypy seems to provide the wrong information for implementation_lower, fix it
+ base["implementation_lower"] = "python"
+ return base
def ensure_directories(self):
- dirs = super(PyPy, self).ensure_directories()
- dirs.add(self.lib_pypy)
- return dirs
+ return super(PyPy, self).ensure_directories() | {self.lib_pypy}
def modules(self):
return [
@@ -51,10 +46,6 @@ class PyPy2(PyPy, Python2):
class PyPy2Posix(PyPy2, PosixSupports):
"""PyPy 2 on POSIX"""
- @property
- def bin_name(self):
- return "bin"
-
def modules(self):
return super(PyPy2Posix, self).modules() + ["posixpath"]
@@ -66,10 +57,6 @@ class PyPy2Posix(PyPy2, PosixSupports):
class Pypy2Windows(PyPy2, WindowsSupports):
"""PyPy 2 on Windows"""
- @property
- def bin_name(self):
- return "Scripts"
-
def modules(self):
return super(Pypy2Windows, self).modules() + ["ntpath"]
diff --git a/src/virtualenv/interpreters/create/pypy/pypy3.py b/src/virtualenv/interpreters/create/pypy/pypy3.py
index 6b759cb..724f0fb 100644
--- a/src/virtualenv/interpreters/create/pypy/pypy3.py
+++ b/src/virtualenv/interpreters/create/pypy/pypy3.py
@@ -5,67 +5,48 @@ import abc
import six
from virtualenv.interpreters.create.support import PosixSupports, Python3Supports, WindowsSupports
-from virtualenv.util.path import Path
from .common import PyPy
@six.add_metaclass(abc.ABCMeta)
class PyPy3(PyPy, Python3Supports):
- """"""
-
@property
def exe_base(self):
return "pypy3"
+ @property
+ def stdlib(self):
+ """
+ PyPy3 seems to respect sysconfig only for the host python...
+ virtual environments purelib is instead lib/pythonx.y
+ """
+ return self.dest_dir / "lib" / "python{}".format(self.interpreter.version_release_str) / "site-packages"
+
def exe_names(self):
base = super(PyPy3, self).exe_names()
base.add("pypy")
return base
- def ensure_directories(self):
- dirs = super(PyPy, self).ensure_directories()
- dirs.add(self.lib_dir / "site-packages")
- return dirs
-
class PyPy3Posix(PyPy3, PosixSupports):
"""PyPy 2 on POSIX"""
@property
- def bin_name(self):
- return "bin"
-
- @property
- def lib_name(self):
- return "lib"
-
- @property
- def lib_base(self):
- return Path(self.lib_name) / self.interpreter.python_name
-
- @property
def _shared_libs(self):
return ["libpypy3-c.so", "libpypy3-c.dylib"]
def _shared_lib_to(self):
- return super(PyPy3, self)._shared_lib_to() + [self.dest_dir / self.lib_name]
+ return super(PyPy3, self)._shared_lib_to() + [self.stdlib.parent.parent]
class Pypy3Windows(PyPy3, WindowsSupports):
"""PyPy 2 on Windows"""
@property
- def bin_name(self):
- return "Scripts"
-
- @property
- def lib_name(self):
- return "Lib"
-
- @property
- def lib_base(self):
- return Path(self.lib_name)
+ def bin_dir(self):
+ """PyPy3 needs to fallback to pypy definition"""
+ return self.dest_dir / "Scripts"
@property
def _shared_libs(self):
diff --git a/src/virtualenv/interpreters/create/venv.py b/src/virtualenv/interpreters/create/venv.py
index afb9471..78d4622 100644
--- a/src/virtualenv/interpreters/create/venv.py
+++ b/src/virtualenv/interpreters/create/venv.py
@@ -13,10 +13,10 @@ from .via_global_ref.api import ViaGlobalRefApi
class Venv(ViaGlobalRefApi):
def __init__(self, options, interpreter):
+ self.builtin_way = options.builtin_way
super(Venv, self).__init__(options, interpreter)
self.can_be_inline = interpreter is CURRENT and interpreter.executable == interpreter.system_executable
self._context = None
- self.builtin_way = options.builtin_way
def _args(self):
return super(Venv, self)._args() + (
@@ -33,9 +33,8 @@ class Venv(ViaGlobalRefApi):
else:
self.create_via_sub_process()
# TODO: cleanup activation scripts
- if self.builtin_way is not None:
- for site_package in self.builtin_way.site_packages:
- ensure_dir(site_package)
+ for lib in self.libs:
+ ensure_dir(lib)
def create_inline(self):
from venv import EnvBuilder
@@ -66,27 +65,10 @@ class Venv(ViaGlobalRefApi):
super(Venv, self).set_pyenv_cfg()
self.pyenv_cfg.update(venv_content)
- def _proxy_builtin_way(self, key):
- if self.builtin_way is None:
- return None
- return getattr(self.builtin_way, key)
-
- @property
- def exe(self):
- return self._proxy_builtin_way("exe")
-
- @property
- def site_packages(self):
- return self._proxy_builtin_way("site_packages")
-
- @property
- def bin_dir(self):
- return self._proxy_builtin_way("bin_dir")
-
- @property
- def bin_name(self):
- return self._proxy_builtin_way("bin_name")
-
- @property
- def lib_dir(self):
- return self._proxy_builtin_way("lib_dir")
+ def __getattribute__(self, item):
+ builtin = object.__getattribute__(self, "builtin_way")
+ if builtin is not None and hasattr(builtin, item):
+ element = getattr(builtin, item)
+ if not callable(element):
+ return element
+ return object.__getattribute__(self, item)
diff --git a/src/virtualenv/interpreters/create/via_global_ref/python2.py b/src/virtualenv/interpreters/create/via_global_ref/python2.py
index 4ff6e84..9c7222a 100644
--- a/src/virtualenv/interpreters/create/via_global_ref/python2.py
+++ b/src/virtualenv/interpreters/create/via_global_ref/python2.py
@@ -27,16 +27,16 @@ class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports):
self.add_module(module)
# 2. install a patched site-package, the default Python 2 site.py is not smart enough to understand pyvenv.cfg,
# so we inject a small shim that can do this
- site_py = self.lib_dir / "site.py"
- relative_site_packages = [
- os.path.relpath(six.ensure_text(str(s)), six.ensure_text(str(site_py))) for s in self.site_packages
- ]
+ site_py = self.stdlib / "site.py"
custom_site = get_custom_site()
if IS_ZIPAPP:
custom_site_text = read_from_zipapp(custom_site)
else:
custom_site_text = custom_site.read_text()
- site_py.write_text(custom_site_text.replace("___EXPECTED_SITE_PACKAGES___", json.dumps(relative_site_packages)))
+ expected = json.dumps(
+ [os.path.relpath(six.ensure_text(str(i)), six.ensure_text(str(site_py))) for i in self.libs]
+ )
+ site_py.write_text(custom_site_text.replace("___EXPECTED_SITE_PACKAGES___", expected))
@abc.abstractmethod
def modules(self):
@@ -48,10 +48,10 @@ class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports):
def add_module(self, req):
for ext in ["py", "pyc"]:
file_path = "{}.{}".format(req, ext)
- self.copier(self.system_stdlib / file_path, self.lib_dir / file_path)
+ self.copier(self.system_stdlib / file_path, self.stdlib / file_path)
def add_folder(self, folder):
- self.copier(self.system_stdlib / folder, self.lib_dir / folder)
+ self.copier(self.system_stdlib / folder, self.stdlib / folder)
def get_custom_site():
diff --git a/src/virtualenv/interpreters/create/via_global_ref/via_global_self_do.py b/src/virtualenv/interpreters/create/via_global_ref/via_global_self_do.py
index 7e259f3..5844a92 100644
--- a/src/virtualenv/interpreters/create/via_global_ref/via_global_self_do.py
+++ b/src/virtualenv/interpreters/create/via_global_ref/via_global_self_do.py
@@ -9,7 +9,7 @@ from six import add_metaclass
from virtualenv.info import is_fs_case_sensitive
from virtualenv.interpreters.create.builtin_way import VirtualenvBuiltin
-from virtualenv.util.path import Path, copy, ensure_dir, make_exe, symlink
+from virtualenv.util.path import copy, ensure_dir, make_exe, symlink
@add_metaclass(ABCMeta)
@@ -42,9 +42,7 @@ class ViaGlobalRefVirtualenvBuiltin(VirtualenvBuiltin):
self.pyenv_cfg["base-executable"] = self.interpreter.system_executable
def ensure_directories(self):
- dirs = {self.dest_dir, self.bin_dir, self.lib_dir}
- dirs.update(self.site_packages)
- return dirs
+ return {self.dest_dir, self.bin_dir, self.script_dir, self.stdlib} | set(self.libs)
def setup_python(self):
aliases = method = self.add_exe_method()
@@ -79,19 +77,3 @@ class ViaGlobalRefVirtualenvBuiltin(VirtualenvBuiltin):
def symlink_exe(src, dest):
symlink(src, dest)
make_exe(dest)
-
- @property
- def lib_base(self):
- raise NotImplementedError
-
- @property
- def system_stdlib(self):
- return Path(self.interpreter.system_prefix) / self.lib_base
-
- @property
- def lib_dir(self):
- return self.dest_dir / self.lib_base
-
- @abc.abstractmethod
- def lib_name(self):
- raise NotImplementedError
diff --git a/src/virtualenv/interpreters/discovery/py_info.py b/src/virtualenv/interpreters/discovery/py_info.py
index 9feecb4..0aebece 100644
--- a/src/virtualenv/interpreters/discovery/py_info.py
+++ b/src/virtualenv/interpreters/discovery/py_info.py
@@ -10,8 +10,12 @@ import logging
import os
import pipes
import platform
+import re
import sys
+import sysconfig
from collections import OrderedDict, namedtuple
+from distutils.command.install import SCHEME_KEYS
+from distutils.dist import Distribution
VersionInfo = namedtuple("VersionInfo", ["major", "minor", "micro", "releaselevel", "serial"])
@@ -21,36 +25,41 @@ def _get_path_extensions():
EXTENSIONS = _get_path_extensions()
+_CONF_VAR_RE = re.compile(r"\{\w+\}")
class PythonInfo(object):
"""Contains information for a Python interpreter"""
def __init__(self):
+ def u(v):
+ return v.decode("utf-8") if isinstance(v, bytes) else v
+
# qualifies the python
- self.platform = sys.platform
- self.implementation = platform.python_implementation()
- self.pypy_version_info = tuple(sys.pypy_version_info) if self.implementation == "PyPy" else None
+ self.platform = u(sys.platform)
+ self.implementation = u(platform.python_implementation())
+ if self.implementation == "PyPy":
+ self.pypy_version_info = tuple(u(i) for i in sys.pypy_version_info)
# this is a tuple in earlier, struct later, unify to our own named tuple
- self.version_info = VersionInfo(*list(sys.version_info))
+ self.version_info = VersionInfo(*list(u(i) for i in sys.version_info))
self.architecture = 64 if sys.maxsize > 2 ** 32 else 32
- self.executable = sys.executable # executable we were called with
- self.original_executable = self.executable
- self.base_executable = getattr(sys, "_base_executable", None) # some platforms may set this
+ self.executable = u(sys.executable) # executable we were called with
+ self.original_executable = u(self.executable)
+ self.base_executable = u(getattr(sys, "_base_executable", None)) # some platforms may set this
- self.version = sys.version
- self.os = os.name
+ self.version = u(sys.version)
+ self.os = u(os.name)
# information about the prefix - determines python home
- self.prefix = getattr(sys, "prefix", None) # prefix we think
- self.base_prefix = getattr(sys, "base_prefix", None) # venv
- self.real_prefix = getattr(sys, "real_prefix", None) # old virtualenv
+ self.prefix = u(getattr(sys, "prefix", None)) # prefix we think
+ self.base_prefix = u(getattr(sys, "base_prefix", None)) # venv
+ self.real_prefix = u(getattr(sys, "real_prefix", None)) # old virtualenv
# information about the exec prefix - dynamic stdlib modules
- self.base_exec_prefix = getattr(sys, "base_exec_prefix", None)
- self.exec_prefix = getattr(sys, "exec_prefix", None)
+ self.base_exec_prefix = u(getattr(sys, "base_exec_prefix", None))
+ self.exec_prefix = u(getattr(sys, "exec_prefix", None))
try:
__import__("venv")
@@ -58,9 +67,28 @@ class PythonInfo(object):
except ImportError:
has = False
self.has_venv = has
- self.path = sys.path
- self.file_system_encoding = sys.getfilesystemencoding()
- self.stdout_encoding = getattr(sys.stdout, "encoding", None)
+ self.path = [u(i) for i in sys.path]
+ self.file_system_encoding = u(sys.getfilesystemencoding())
+ 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()}
+ config_var_keys = set()
+ for element in self.sysconfig_paths.values():
+ for k in _CONF_VAR_RE.findall(element):
+ config_var_keys.add(u(k[1:-1]))
+ self.sysconfig_config_vars = {u(i): u(sysconfig.get_config_var(i)) for i in config_var_keys}
+
+ self.distutils_install = {u(k): u(v) for k, v in self._distutils_install().items()}
+
+ def _distutils_install(self):
+ # follow https://github.com/pypa/pip/blob/master/src/pip/_internal/locations.py#L95
+ d = Distribution({"script_args": "--no-user-cfg"})
+ d.parse_config_files()
+ i = d.get_command_obj("install", create=True)
+ i.prefix = "a"
+ i.finalize_options()
+ result = {key: (getattr(i, "install_{}".format(key))[1:]).lstrip(os.sep) for key in SCHEME_KEYS}
+ return result
@property
def version_str(self):
@@ -132,8 +160,7 @@ class PythonInfo(object):
data = json.loads(payload)
data["version_info"] = VersionInfo(**data["version_info"]) # restore this to a named tuple structure
result = cls()
- for var in vars(result):
- setattr(result, var, data[var])
+ result.__dict__ = {k: v for k, v in data.items()}
return result
@property
@@ -249,7 +276,7 @@ class PythonInfo(object):
cmd = cls._get_exe_cmd(exe)
# noinspection DuplicatedCode
# this is duplicated here because this file is executed on its own, so cannot be refactored otherwise
- logging.debug("get interpreter info via cmd: %s", Cmd(cmd))
+ logging.debug(u"get interpreter info via cmd: %s", Cmd(cmd))
try:
process = Popen(
cmd, universal_newlines=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE
@@ -307,6 +334,16 @@ class PythonInfo(object):
return False
return True
+ def sysconfig_path(self, key, config_var=None, sep=os.sep):
+ pattern = self.sysconfig_paths[key]
+ if config_var is None:
+ config_var = self.sysconfig_config_vars
+ else:
+ base = {k: v for k, v in self.sysconfig_config_vars.items()}
+ base.update(config_var)
+ config_var = base
+ return pattern.format(**config_var).replace(u"/", sep)
+
class Cmd(object):
def __init__(self, cmd, env=None):
@@ -314,9 +351,12 @@ class Cmd(object):
self.env = env
def __repr__(self):
- cmd_repr = " ".join(pipes.quote(c) for c in self.cmd)
+ def e(v):
+ return v.decode("utf-8") if isinstance(v, bytes) else v
+
+ cmd_repr = e(" ").join(pipes.quote(e(c)) for c in self.cmd)
if self.env is not None:
- cmd_repr += " env of {!r}".format(self.env)
+ cmd_repr += e(" env of {!r}").format(self.env)
return cmd_repr
diff --git a/src/virtualenv/pyenv_cfg.py b/src/virtualenv/pyenv_cfg.py
index 05daa5a..218c759 100644
--- a/src/virtualenv/pyenv_cfg.py
+++ b/src/virtualenv/pyenv_cfg.py
@@ -54,3 +54,6 @@ class PyEnvCfg(object):
def update(self, other):
self.content.update(other)
return self
+
+ def __repr__(self):
+ return "{}(path={})".format(self.__class__.__name__, self.path)
diff --git a/src/virtualenv/seed/via_app_data/pip_install/base.py b/src/virtualenv/seed/via_app_data/pip_install/base.py
index ba65e9a..7082cf6 100644
--- a/src/virtualenv/seed/via_app_data/pip_install/base.py
+++ b/src/virtualenv/seed/via_app_data/pip_install/base.py
@@ -34,9 +34,8 @@ class PipInstall(object):
def install(self):
self._extracted = True
# sync image
- site_package = self._creator.site_packages[0]
for filename in self._image_dir.iterdir():
- into = site_package / filename.name
+ into = self._creator.purelib / filename.name
logging.debug("%s %s from %s", self.__class__.__name__, into, filename)
if into.exists():
if into.is_dir() and not into.is_symlink():
@@ -46,9 +45,9 @@ class PipInstall(object):
self._sync(filename, into)
# generate console executables
consoles = set()
- bin_dir = self._creator.bin_dir
+ script_dir = self._creator.script_dir
for name, module in self._console_scripts.items():
- consoles.update(self._create_console_entry_point(name, module, bin_dir))
+ consoles.update(self._create_console_entry_point(name, module, script_dir))
logging.debug("generated console scripts %s", " ".join(i.name for i in consoles))
def build_image(self):
@@ -83,7 +82,7 @@ class PipInstall(object):
try:
to_folder = Path(folder)
rel = os.path.relpath(
- six.ensure_text(str(self._creator.bin_dir)), six.ensure_text(str(self._creator.site_packages[0]))
+ six.ensure_text(str(self._creator.script_dir)), six.ensure_text(str(self._creator.purelib))
)
for name, module in self._console_scripts.items():
new_files.update(
diff --git a/src/virtualenv/util/path/_permission.py b/src/virtualenv/util/path/_permission.py
index 1356b6d..85f93b8 100644
--- a/src/virtualenv/util/path/_permission.py
+++ b/src/virtualenv/util/path/_permission.py
@@ -13,7 +13,7 @@ def make_exe(filename):
mode |= level
filename.chmod(mode)
break
- except PermissionError:
+ except OSError:
continue
diff --git a/src/virtualenv/util/path/_sync.py b/src/virtualenv/util/path/_sync.py
index 860aee1..4c5aa86 100644
--- a/src/virtualenv/util/path/_sync.py
+++ b/src/virtualenv/util/path/_sync.py
@@ -23,6 +23,10 @@ def symlink_or_copy(do_copy, src, dst, relative_symlinks_ok=False):
"""
Try symlinking a target, and if that fails, fall back to copying.
"""
+ if not src.exists():
+ raise RuntimeError("source {} does not exists".format(src))
+ if src == dst:
+ raise RuntimeError("source {} is same as destination ".format(src))
def norm(val):
if IS_PYPY and six.PY3:
diff --git a/src/virtualenv/util/subprocess/_win_subprocess.py b/src/virtualenv/util/subprocess/_win_subprocess.py
index e8fdaf0..f30344d 100644
--- a/src/virtualenv/util/subprocess/_win_subprocess.py
+++ b/src/virtualenv/util/subprocess/_win_subprocess.py
@@ -5,7 +5,6 @@
import ctypes
import os
import subprocess
-import sys
from ctypes import Structure, WinError, byref, c_char_p, c_void_p, c_wchar, c_wchar_p, sizeof, windll
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPVOID, LPWSTR, WORD