def test_discover_ok(tmp_path, monkeypatch, suffix, impl, version, arch, into, caplog, session_app_data): caplog.set_level(logging.DEBUG) folder = tmp_path / into folder.mkdir(parents=True, exist_ok=True) name = f"{impl}{version}" if arch: name += f"-{arch}" name += suffix dest = folder / name os.symlink(CURRENT.executable, str(dest)) pyvenv = Path(CURRENT.executable).parents[1] / "pyvenv.cfg" if pyvenv.exists(): (folder / pyvenv.name).write_text(pyvenv.read_text()) inside_folder = str(tmp_path) base = CURRENT.discover_exe(session_app_data, inside_folder) found = base.executable dest_str = str(dest) if not fs_is_case_sensitive(): found = found.lower() dest_str = dest_str.lower() assert found == dest_str assert len(caplog.messages) >= 1, caplog.text assert "get interpreter info via cmd: " in caplog.text dest.rename(dest.parent / (dest.name + "-1")) CURRENT._cache_exe_discovery.clear() with pytest.raises(RuntimeError): CURRENT.discover_exe(session_app_data, inside_folder)
def test_python_path(monkeypatch, tmp_path, python_path_on): result = cli_run([ensure_text(str(tmp_path)), "--without-pip", "--activators", ""]) monkeypatch.chdir(tmp_path) case_sensitive = fs_is_case_sensitive() def _get_sys_path(flag=None): cmd = [str(result.creator.exe)] if flag: cmd.append(flag) cmd.extend(["-c", "import json; import sys; print(json.dumps(sys.path))"]) return [i if case_sensitive else i.lower() for i in json.loads(subprocess.check_output(cmd))] monkeypatch.delenv(str("PYTHONPATH"), raising=False) base = _get_sys_path() # note the value result.creator.interpreter.system_stdlib cannot be set, as that would disable our custom site.py python_paths = [ str(Path(result.creator.interpreter.prefix)), str(Path(result.creator.interpreter.system_stdlib) / "b"), str(result.creator.purelib / "a"), str(result.creator.purelib), str(result.creator.bin_dir), str(tmp_path / "base"), str(tmp_path / "base_sep") + os.sep, "name", "name{}".format(os.sep), str(tmp_path.parent / (ensure_text(tmp_path.name) + "_suffix")), ".", "..", "", ] python_path_env = os.pathsep.join(ensure_str(i) for i in python_paths) monkeypatch.setenv(str("PYTHONPATH"), python_path_env) extra_all = _get_sys_path(None if python_path_on else "-E") if python_path_on: assert extra_all[0] == "" # the cwd is always injected at start as '' extra_all = extra_all[1:] assert base[0] == "" base = base[1:] assert not (set(base) - set(extra_all)) # all base paths are present abs_python_paths = list(OrderedDict((os.path.abspath(ensure_text(i)), None) for i in python_paths).keys()) abs_python_paths = [i if case_sensitive else i.lower() for i in abs_python_paths] extra_as_python_path = extra_all[: len(abs_python_paths)] assert abs_python_paths == extra_as_python_path # python paths are there at the start non_python_path = extra_all[len(abs_python_paths) :] assert non_python_path == [i for i in base if i not in extra_as_python_path] else: assert base == extra_all
def test_get_site_packages(tmp_path): case_sensitive = fs_is_case_sensitive() session = cli_run([ensure_text(str(tmp_path))]) env_site_packages = [str(session.creator.purelib), str(session.creator.platlib)] out = subprocess.check_output( [str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"], universal_newlines=True, ) site_packages = ast.literal_eval(out) if not case_sensitive: env_site_packages = [x.lower() for x in env_site_packages] site_packages = [x.lower() for x in site_packages] for env_site_package in env_site_packages: assert env_site_package in site_packages
def generate_names(self): impls = OrderedDict() if self.implementation: impls[self.implementation] = False if fs_is_case_sensitive(): impls[self.implementation.lower()] = False impls[self.implementation.upper()] = False impls["python"] = True version = self.major, self.minor, self.micro try: version = version[: version.index(None)] except ValueError: pass for impl, match in impls.items(): for at in range(len(version), -1, -1): cur_ver = version[0:at] spec = "{}{}".format(impl, ".".join(str(i) for i in cur_ver)) yield spec, match
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
def _possible_base(self): possible_base = OrderedDict() basename = os.path.splitext(os.path.basename(self.executable))[0].rstrip(digits) possible_base[basename] = None possible_base[self.implementation] = None # python is always the final option as in practice is used by multiple implementation as exe name if "python" in possible_base: del possible_base["python"] possible_base["python"] = None for base in possible_base: lower = base.lower() yield lower from virtualenv.info import fs_is_case_sensitive if fs_is_case_sensitive(): if base != lower: yield base upper = base.upper() if upper != base: yield upper
def generate_names(self): impls = OrderedDict() if self.implementation: # first consider implementation as it is impls[self.implementation] = False if fs_is_case_sensitive(): # for case sensitive file systems consider lower and upper case versions too # trivia: MacBooks and all pre 2018 Windows-es were case insensitive by default impls[self.implementation.lower()] = False impls[self.implementation.upper()] = False impls["python"] = True # finally consider python as alias, implementation must match now version = self.major, self.minor, self.micro try: version = version[: version.index(None)] except ValueError: pass for impl, match in impls.items(): for at in range(len(version), -1, -1): cur_ver = version[0:at] spec = f"{impl}{'.'.join(str(i) for i in cur_ver)}" yield spec, match
def test_discover_ok(tmp_path, monkeypatch, suffix, impl, version, arch, into, caplog): caplog.set_level(logging.DEBUG) folder = tmp_path / into folder.mkdir(parents=True, exist_ok=True) dest = folder / "{}{}".format(impl, version, arch, suffix) os.symlink(CURRENT.executable, str(dest)) inside_folder = str(tmp_path) base = CURRENT.discover_exe(inside_folder) found = base.executable dest_str = str(dest) if not fs_is_case_sensitive(): found = found.lower() dest_str = dest_str.lower() assert found == dest_str assert len(caplog.messages) >= 1, caplog.text assert "get interpreter info via cmd: " in caplog.text dest.rename(dest.parent / (dest.name + "-1")) CURRENT._cache_exe_discovery.clear() with pytest.raises(RuntimeError): CURRENT.discover_exe(inside_folder)
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
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