Skip to content

separate describe/create - check upfront if can create #1502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ virtualenv.activate =
python = virtualenv.activation.python:PythonActivator
xonsh = virtualenv.activation.xonsh:XonshActivator
virtualenv.create =
venv = virtualenv.interpreters.create.venv:Venv
cpython3-posix = virtualenv.interpreters.create.cpython.cpython3:CPython3Posix
cpython3-win = virtualenv.interpreters.create.cpython.cpython3:CPython3Windows
cpython2-posix = virtualenv.interpreters.create.cpython.cpython2:CPython2Posix
cpython2-win = virtualenv.interpreters.create.cpython.cpython2:CPython2Windows
pypy2-posix = virtualenv.interpreters.create.pypy.pypy2:PyPy2Posix
pypy2-win = virtualenv.interpreters.create.pypy.pypy2:Pypy2Windows
pypy3-posix = virtualenv.interpreters.create.pypy.pypy3:PyPy3Posix
pypy3-win = virtualenv.interpreters.create.pypy.pypy3:Pypy3Windows
venv = virtualenv.create.via_global_ref.venv:Venv
cpython3-posix = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Posix
cpython3-win = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Windows
cpython2-posix = virtualenv.create.via_global_ref.builtin.cpython.cpython2:CPython2Posix
cpython2-win = virtualenv.create.via_global_ref.builtin.cpython.cpython2:CPython2Windows
pypy2-posix = virtualenv.create.via_global_ref.builtin.pypy.pypy2:PyPy2Posix
pypy2-win = virtualenv.create.via_global_ref.builtin.pypy.pypy2:Pypy2Windows
pypy3-posix = virtualenv.create.via_global_ref.builtin.pypy.pypy3:PyPy3Posix
pypy3-win = virtualenv.create.via_global_ref.builtin.pypy.pypy3:Pypy3Windows
virtualenv.discovery =
builtin = virtualenv.interpreters.discovery.builtin:Builtin
builtin = virtualenv.discovery.builtin:Builtin
virtualenv.seed =
none = virtualenv.seed.none:NoneSeeder
pip = virtualenv.seed.embed.pip_invoke:PipInvoke
Expand Down
4 changes: 2 additions & 2 deletions src/virtualenv/activation/via_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ def generate(self, creator):
def replacements(self, creator, dest_folder):
return {
"__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt,
"__VIRTUAL_ENV__": six.ensure_text(str(creator.dest_dir)),
"__VIRTUAL_ENV__": six.ensure_text(str(creator.dest)),
"__VIRTUAL_NAME__": creator.env_name,
"__BIN_NAME__": six.ensure_text(str(creator.bin_dir.relative_to(creator.dest_dir))),
"__BIN_NAME__": six.ensure_text(str(creator.bin_dir.relative_to(creator.dest))),
"__PATH_SEP__": os.pathsep,
}

Expand Down
4 changes: 2 additions & 2 deletions src/virtualenv/config/ini.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

import six

from virtualenv.info import PY3, get_default_config_dir
from virtualenv.info import PY3, default_config_dir
from virtualenv.util import ConfigParser
from virtualenv.util.path import Path

from .convert import convert

DEFAULT_CONFIG_FILE = get_default_config_dir() / "virtualenv.ini"
DEFAULT_CONFIG_FILE = default_config_dir() / "virtualenv.ini"


class IniConfig(object):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
import six
from six import add_metaclass

from virtualenv.discovery.py_info import Cmd
from virtualenv.info import IS_ZIPAPP
from virtualenv.interpreters.discovery.py_info import Cmd
from virtualenv.pyenv_cfg import PyEnvCfg
from virtualenv.util.path import Path
from virtualenv.util.subprocess import run_cmd
Expand All @@ -31,59 +31,10 @@ class Creator(object):
def __init__(self, options, interpreter):
self.interpreter = interpreter
self._debug = None
self.dest_dir = Path(options.dest_dir)
self.dest = Path(options.dest)
self.enable_system_site_package = options.system_site
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()
}
self.pyenv_cfg = PyEnvCfg.from_folder(self.dest)

def __repr__(self):
return six.ensure_str(self.__unicode__())
Expand All @@ -93,15 +44,15 @@ def __unicode__(self):

def _args(self):
return [
("dest", six.ensure_text(str(self.dest_dir))),
("dest", six.ensure_text(str(self.dest))),
("global", self.enable_system_site_package),
("clear", self.clear),
]

@classmethod
def add_parser_arguments(cls, parser, interpreter):
def add_parser_arguments(cls, parser, interpreter, meta):
parser.add_argument(
"dest_dir", help="directory to create virtualenv at", type=cls.validate_dest_dir, default="venv", nargs="?",
"dest", help="directory to create virtualenv at", type=cls.validate_dest, default="venv", nargs="?",
)
parser.add_argument(
"--clear",
Expand All @@ -119,7 +70,7 @@ def add_parser_arguments(cls, parser, interpreter):
)

@classmethod
def validate_dest_dir(cls, raw_value):
def validate_dest(cls, raw_value):
"""No path separator in the path, valid chars and must be write-able"""

def non_write_able(dest, value):
Expand Down Expand Up @@ -176,8 +127,8 @@ def non_write_able(dest, value):
return str(value)

def run(self):
if self.dest_dir.exists() and self.clear:
logging.debug("delete %s", self.dest_dir)
if self.dest.exists() and self.clear:
logging.debug("delete %s", self.dest)

def onerror(func, path, exc_info):
if not os.access(path, os.W_OK):
Expand All @@ -186,7 +137,7 @@ def onerror(func, path, exc_info):
else:
raise

shutil.rmtree(str(self.dest_dir), ignore_errors=True, onerror=onerror)
shutil.rmtree(str(self.dest), ignore_errors=True, onerror=onerror)
self.create()
self.set_pyenv_cfg()

Expand All @@ -195,7 +146,8 @@ def create(self):
raise NotImplementedError

@classmethod
def supports(cls, interpreter):
def can_create(cls, interpreter):
"""Default is that we can"""
return True

def set_pyenv_cfg(self):
Expand All @@ -207,10 +159,6 @@ def set_pyenv_cfg(self):
"virtualenv": __version__,
}

@property
def env_name(self):
return six.ensure_text(self.dest_dir.parts[-1])

@property
def debug(self):
if self._debug is None and self.exe is not None:
Expand All @@ -221,10 +169,6 @@ def debug(self):
def debug_script(self):
return DEBUG_SCRIPT

@property
def exe(self):
return None


def get_env_debug_info(env_exe, debug_script):
if IS_ZIPAPP:
Expand Down
106 changes: 106 additions & 0 deletions src/virtualenv/create/describe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from __future__ import absolute_import, print_function, unicode_literals

from abc import ABCMeta
from collections import OrderedDict

from six import add_metaclass, ensure_text

from virtualenv.info import IS_WIN
from virtualenv.util.path import Path


@add_metaclass(ABCMeta)
class Describe(object):
"""Given a host interpreter tell us information about what the created interpreter might look like"""

suffix = ".exe" if IS_WIN else ""

def __init__(self, dest, interpreter):
self.interpreter = interpreter
self.dest = dest
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 / Path(self.interpreter.distutils_install["scripts"])

@property
def purelib(self):
return self.dest / self.interpreter.distutils_install["purelib"]

@property
def platlib(self):
return self.dest / 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 _config_vars(self):
if self._conf_vars is None:
self._conf_vars = self._calc_config_vars(ensure_text(str(self.dest)))
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_vars.items()
}

@classmethod
def can_describe(cls, interpreter):
"""Knows means it knows how the output will look"""
return True

@property
def env_name(self):
return ensure_text(self.dest.parts[-1])

@property
def exe(self):
return self.bin_dir / "{}{}".format(self.exe_stem(), self.suffix)

@classmethod
def exe_stem(cls):
"""executable name without suffix - there seems to be no standard way to get this without creating it"""
raise NotImplementedError


@add_metaclass(ABCMeta)
class Python2Supports(Describe):
@classmethod
def can_describe(cls, interpreter):
return interpreter.version_info.major == 2 and super(Python2Supports, cls).can_describe(interpreter)


@add_metaclass(ABCMeta)
class Python3Supports(Describe):
@classmethod
def can_describe(cls, interpreter):
return interpreter.version_info.major == 3 and super(Python3Supports, cls).can_describe(interpreter)


@add_metaclass(ABCMeta)
class PosixSupports(Describe):
@classmethod
def can_describe(cls, interpreter):
return interpreter.os == "posix" and super(PosixSupports, cls).can_describe(interpreter)


@add_metaclass(ABCMeta)
class WindowsSupports(Describe):
@classmethod
def can_describe(cls, interpreter):
return interpreter.os == "nt" and super(WindowsSupports, cls).can_describe(interpreter)
36 changes: 36 additions & 0 deletions src/virtualenv/create/via_global_ref/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import absolute_import, unicode_literals

from abc import ABCMeta

from six import add_metaclass

from ..creator import Creator


@add_metaclass(ABCMeta)
class ViaGlobalRefApi(Creator):
def __init__(self, options, interpreter):
super(ViaGlobalRefApi, self).__init__(options, interpreter)
self.symlinks = getattr(options, "copies", False) is False

@classmethod
def add_parser_arguments(cls, parser, interpreter, meta):
super(ViaGlobalRefApi, cls).add_parser_arguments(parser, interpreter, meta)
group = parser.add_mutually_exclusive_group()
if meta.can_symlink:
group.add_argument(
"--symlinks",
default=True,
action="store_true",
dest="symlinks",
help="try to use symlinks rather than copies, when symlinks are not the default for the platform",
)
if meta.can_copy:
group.add_argument(
"--copies",
"--always-copy",
default=not meta.can_symlink,
action="store_true",
dest="copies",
help="try to use copies rather than symlinks, even when symlinks are the default for the platform",
)
17 changes: 17 additions & 0 deletions src/virtualenv/create/via_global_ref/builtin/builtin_way.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import absolute_import, unicode_literals

from abc import ABCMeta

from six import add_metaclass

from virtualenv.create.creator import Creator
from virtualenv.create.describe import Describe


@add_metaclass(ABCMeta)
class VirtualenvBuiltin(Creator, Describe):
"""A creator that does operations itself without delegation, if we can create it we can also describe it"""

def __init__(self, options, interpreter):
Creator.__init__(self, options, interpreter)
Describe.__init__(self, self.dest, interpreter)
Loading