def can_create(cls, interpreter): """By default all built-in methods assume that if we can describe it we can create it""" # first we must be able to describe it if cls.can_describe(interpreter): sources = [] can_copy = True can_symlink = fs_supports_symlink() for src in cls.sources(interpreter): if src.exists: if can_copy and not src.can_copy: can_copy = False logging.debug("%s cannot copy %s", cls.__name__, src) if can_symlink and not src.can_symlink: can_symlink = False logging.debug("%s cannot symlink %s", cls.__name__, src) if not (can_copy or can_symlink): break else: logging.debug("%s missing %s", cls.__name__, src) break sources.append(src) else: return Meta(sources, can_copy, can_symlink) return None
def add_parser_arguments(cls, parser, interpreter, app_data): super().add_parser_arguments(parser, interpreter, app_data) can_symlink = app_data.transient is False and fs_supports_symlink() sym = "" if can_symlink else "not supported - " parser.add_argument( "--symlink-app-data", dest="symlink_app_data", action="store_true" if can_symlink else "store_false", help= f"{sym} symlink the python packages from the app-data folder (requires seed pip>=19.3)", default=False, )
def add_parser_arguments(cls, parser, interpreter): super(FromAppData, cls).add_parser_arguments(parser, interpreter) parser.add_argument( "--clear-app-data", dest="clear_app_data", action="store_true", help="clear the app data folder of seed images ({})".format((default_data_dir() / "seed-v1").path), default=False, ) can_symlink = fs_supports_symlink() parser.add_argument( "--symlink-app-data", dest="symlink_app_data", action="store_true" if can_symlink else "store_false", help="{} symlink the python packages from the app-data folder (requires seed pip>=19.3)".format( "" if can_symlink else "not supported - " ), default=False, )
class PathRef(object): FS_SUPPORTS_SYMLINK = fs_supports_symlink() FS_CASE_SENSITIVE = fs_is_case_sensitive() def __init__(self, src): self.src = src self.exists = src.exists() self._can_read = None if self.exists else False self._can_copy = None if self.exists else False self._can_symlink = None if self.exists else False def __repr__(self): return "{}(src={})".format(self.__class__.__name__, self.src) @property def can_read(self): if self._can_read is None: if self.src.is_file(): try: with self.src.open("rb"): self._can_read = True except OSError: self._can_read = False else: self._can_read = os.access(ensure_text(str(self.src)), os.R_OK) return self._can_read @property def can_copy(self): if self._can_copy is None: self._can_copy = self.can_read return self._can_copy @property def can_symlink(self): if self._can_symlink is None: self._can_symlink = self.FS_SUPPORTS_SYMLINK and self.can_read return self._can_symlink @abstractmethod def run(self, creator, symlinks): raise NotImplementedError
@pytest.mark.parametrize( "creator, method, isolated", [ pytest.param( *i, marks=pytest.mark.xfail( reason="https://bitbucket.org/pypy/pypy/issues/3159/pypy36-730-venv-fails-with-copies-on-linux", strict=True, ) ) if _VENV_BUG_ON and i[0] == "venv" and i[1] == "copies" else i for i in product( CURRENT_CREATORS, (["copies"] + (["symlinks"] if fs_supports_symlink() else [])), ["isolated", "global"] ) ], ) def test_create_no_seed(python, creator, isolated, system, coverage_env, special_name_dir, method): dest = special_name_dir cmd = [ "-v", "-v", "-p", ensure_text(python), ensure_text(str(dest)), "--without-pip", "--activators", "", "--creator",
import pytest from virtualenv.discovery.py_info import PythonInfo from virtualenv.info import fs_supports_symlink from virtualenv.run import cli_run from virtualenv.seed.embed.wheels import BUNDLE_SUPPORT from virtualenv.seed.embed.wheels.acquire import BUNDLE_FOLDER from virtualenv.util.six import ensure_text from virtualenv.util.subprocess import Popen @pytest.mark.slow @pytest.mark.timeout(timeout=120) @pytest.mark.parametrize("copies", [False, True] if fs_supports_symlink() else [True]) def test_seed_link_via_app_data(tmp_path, coverage_env, current_fastest, copies): current = PythonInfo.current_system() bundle_ver = BUNDLE_SUPPORT[current.version_release_str] create_cmd = [ ensure_text( str(tmp_path / "en v") ), # space in the name to ensure generated scripts work when path has space "--seeder", "app-data", "--extra-search-dir", ensure_text(str(BUNDLE_FOLDER)), "--download", "--pip", bundle_ver["pip"].split("-")[1],
def test_satisfy_not_version(spec): parsed_spec = PythonSpec.from_string_spec("{}{}".format(CURRENT.implementation, spec)) matches = CURRENT.satisfies(parsed_spec, True) assert matches is False def test_py_info_cached_error(mocker, tmp_path, session_app_data): spy = mocker.spy(cached_py_info, "_run_subprocess") with pytest.raises(RuntimeError): PythonInfo.from_exe(str(tmp_path), session_app_data) with pytest.raises(RuntimeError): PythonInfo.from_exe(str(tmp_path), session_app_data) assert spy.call_count == 1 @pytest.mark.skipif(not fs_supports_symlink(), reason="symlink is not supported") def test_py_info_cached_symlink_error(mocker, tmp_path, session_app_data): spy = mocker.spy(cached_py_info, "_run_subprocess") with pytest.raises(RuntimeError): PythonInfo.from_exe(str(tmp_path), session_app_data) symlinked = tmp_path / "a" symlinked.symlink_to(tmp_path) with pytest.raises(RuntimeError): PythonInfo.from_exe(str(symlinked), session_app_data) assert spy.call_count == 2 def test_py_info_cache_clear(mocker, tmp_path, session_app_data): spy = mocker.spy(cached_py_info, "_run_subprocess") result = PythonInfo.from_exe(sys.executable, session_app_data) assert result is not None
def can_create(cls, interpreter): if interpreter.has_venv: return Meta(can_symlink=fs_supports_symlink(), can_copy=True) return None
parsed_spec = PythonSpec.from_string_spec("{}{}".format( CURRENT.implementation, spec)) matches = CURRENT.satisfies(parsed_spec, True) assert matches is False def test_py_info_cached_error(mocker, tmp_path, session_app_data): spy = mocker.spy(cached_py_info, "_run_subprocess") with pytest.raises(RuntimeError): PythonInfo.from_exe(str(tmp_path), session_app_data) with pytest.raises(RuntimeError): PythonInfo.from_exe(str(tmp_path), session_app_data) assert spy.call_count == 1 @pytest.mark.skipif(not fs_supports_symlink(), reason="symlink is not supported") def test_py_info_cached_symlink_error(mocker, tmp_path, session_app_data): spy = mocker.spy(cached_py_info, "_run_subprocess") with pytest.raises(RuntimeError): PythonInfo.from_exe(str(tmp_path), session_app_data) symlinked = tmp_path / "a" symlinked.symlink_to(tmp_path) with pytest.raises(RuntimeError): PythonInfo.from_exe(str(symlinked), session_app_data) assert spy.call_count == 2 def test_py_info_cache_clear(mocker, tmp_path, session_app_data): spy = mocker.spy(cached_py_info, "_run_subprocess") assert PythonInfo.from_exe(sys.executable, session_app_data) is not None
and CURRENT.pypy_version_info[0:2] == [7, 3] and CURRENT.platform == "linux") @pytest.mark.parametrize( "creator, method, isolated", [ pytest.param( *i, marks=pytest.mark.xfail( reason= "https://bitbucket.org/pypy/pypy/issues/3159/pypy36-730-venv-fails-with-copies-on-linux", strict=True, )) if _VENV_BUG_ON and i[0] == "venv" and i[1] == "copies" else i for i in product(CURRENT_CREATORS, ( ["copies"] + (["symlinks"] if fs_supports_symlink() else []) ), ["isolated", "global"]) ], ) def test_create_no_seed(python, creator, isolated, system, coverage_env, special_name_dir, method): dest = special_name_dir cmd = [ "-v", "-v", "-p", ensure_text(python), ensure_text(str(dest)), "--without-pip", "--activators", "",
class PathRef(object): """Base class that checks if a file reference can be symlink/copied""" FS_SUPPORTS_SYMLINK = fs_supports_symlink() FS_CASE_SENSITIVE = fs_is_case_sensitive() def __init__(self, src, must_symlink, must_copy): self.must_symlink = must_symlink self.must_copy = must_copy self.src = src try: self.exists = src.exists() except OSError: self.exists = False self._can_read = None if self.exists else False self._can_copy = None if self.exists else False self._can_symlink = None if self.exists else False if self.must_copy is True and self.must_symlink is True: raise ValueError("can copy and symlink at the same time") def __repr__(self): return "{}(src={})".format(self.__class__.__name__, self.src) @property def can_read(self): if self._can_read is None: if self.src.is_file(): try: with self.src.open("rb"): self._can_read = True except OSError: self._can_read = False else: self._can_read = os.access(ensure_text(str(self.src)), os.R_OK) return self._can_read @property def can_copy(self): if self._can_copy is None: if self.must_symlink: self._can_copy = self.can_symlink else: self._can_copy = self.can_read return self._can_copy @property def can_symlink(self): if self._can_symlink is None: if self.must_copy: self._can_symlink = self.can_copy else: self._can_symlink = self.FS_SUPPORTS_SYMLINK and self.can_read return self._can_symlink @abstractmethod def run(self, creator, symlinks): raise NotImplementedError def method(self, symlinks): if self.must_symlink: return symlink if self.must_copy: return copy return symlink if symlinks else copy
def __init__(self): super(ViaGlobalRefMeta, self).__init__() self.copy_error = None self.symlink_error = None if not fs_supports_symlink(): self.symlink = "the filesystem does not supports symlink"
def has_symlink_support(tmp_path_factory): return fs_supports_symlink()
import sys from stat import S_IREAD, S_IRGRP, S_IROTH, S_IWUSR import pytest from virtualenv.discovery.py_info import PythonInfo from virtualenv.info import fs_supports_symlink from virtualenv.run import cli_run from virtualenv.seed.embed.wheels import BUNDLE_SUPPORT from virtualenv.seed.embed.wheels.acquire import BUNDLE_FOLDER from virtualenv.util.six import ensure_text from virtualenv.util.subprocess import Popen @pytest.mark.slow @pytest.mark.parametrize("copies", [False, True] if fs_supports_symlink() else [True]) def test_base_bootstrap_link_via_app_data(tmp_path, coverage_env, current_fastest, copies): current = PythonInfo.current_system() bundle_ver = BUNDLE_SUPPORT[current.version_release_str] create_cmd = [ ensure_text(str(tmp_path / "en v")), # space in the name to ensure generated scripts work when path has space "--seeder", "app-data", "--extra-search-dir", ensure_text(str(BUNDLE_FOLDER)), "--download", "--pip", bundle_ver["pip"].split("-")[1], "--setuptools", bundle_ver["setuptools"].split("-")[1], "--clear-app-data",
def __init__(self): super().__init__() self.copy_error = None self.symlink_error = None if not fs_supports_symlink(): self.symlink_error = "the filesystem does not supports symlink"
class PathRef(metaclass=ABCMeta): """Base class that checks if a file reference can be symlink/copied""" FS_SUPPORTS_SYMLINK = fs_supports_symlink() FS_CASE_SENSITIVE = fs_is_case_sensitive() def __init__(self, src, must=RefMust.NA, when=RefWhen.ANY): self.must = must self.when = when self.src = src try: self.exists = src.exists() except OSError: self.exists = False self._can_read = None if self.exists else False self._can_copy = None if self.exists else False self._can_symlink = None if self.exists else False def __repr__(self): return f"{self.__class__.__name__}(src={self.src})" @property def can_read(self): if self._can_read is None: if self.src.is_file(): try: with self.src.open("rb"): self._can_read = True except OSError: self._can_read = False else: self._can_read = os.access(str(self.src), os.R_OK) return self._can_read @property def can_copy(self): if self._can_copy is None: if self.must == RefMust.SYMLINK: self._can_copy = self.can_symlink else: self._can_copy = self.can_read return self._can_copy @property def can_symlink(self): if self._can_symlink is None: if self.must == RefMust.COPY: self._can_symlink = self.can_copy else: self._can_symlink = self.FS_SUPPORTS_SYMLINK and self.can_read return self._can_symlink @abstractmethod def run(self, creator, symlinks): # noqa: U100 raise NotImplementedError def method(self, symlinks): if self.must == RefMust.SYMLINK: return symlink if self.must == RefMust.COPY: return copy return symlink if symlinks else copy