codekingpro/portable-devtools
114k
1from __future__ import annotations2 3from abc import ABC4from collections import OrderedDict5from pathlib import Path6 7from virtualenv.info import IS_WIN8 9 10class Describe(ABC):11 """Given a host interpreter tell us information about what the created interpreter might look like."""12 13 suffix = ".exe" if IS_WIN else ""14 15 def __init__(self, dest, interpreter) -> None:16 self.interpreter = interpreter17 self.dest = dest18 self._stdlib = None19 self._stdlib_platform = None20 self._system_stdlib = None21 self._conf_vars = None22 23 @property24 def bin_dir(self):25 return self.script_dir26 27 @property28 def script_dir(self):29 return self.dest / self.interpreter.install_path("scripts")30 31 @property32 def purelib(self):33 return self.dest / self.interpreter.install_path("purelib")34 35 @property36 def platlib(self):37 return self.dest / self.interpreter.install_path("platlib")38 39 @property40 def libs(self):41 return list(OrderedDict(((self.platlib, None), (self.purelib, None))).keys())42 43 @property44 def stdlib(self):45 if self._stdlib is None:46 self._stdlib = Path(self.interpreter.sysconfig_path("stdlib", config_var=self._config_vars))47 return self._stdlib48 49 @property50 def stdlib_platform(self):51 if self._stdlib_platform is None:52 self._stdlib_platform = Path(self.interpreter.sysconfig_path("platstdlib", config_var=self._config_vars))53 return self._stdlib_platform54 55 @property56 def _config_vars(self):57 if self._conf_vars is None:58 self._conf_vars = self._calc_config_vars(self.dest)59 return self._conf_vars60 61 def _calc_config_vars(self, to):62 sys_vars = self.interpreter.sysconfig_vars63 return {k: (to if v is not None and v.startswith(self.interpreter.prefix) else v) for k, v in sys_vars.items()}64 65 @classmethod66 def can_describe(cls, interpreter): # noqa: ARG00367 """Knows means it knows how the output will look."""68 return True69 70 @property71 def env_name(self):72 return self.dest.parts[-1]73 74 @property75 def exe(self):76 return self.bin_dir / f"{self.exe_stem()}{self.suffix}"77 78 @classmethod79 def exe_stem(cls):80 """Executable name without suffix - there seems to be no standard way to get this without creating it."""81 raise NotImplementedError82 83 def script(self, name):84 return self.script_dir / f"{name}{self.suffix}"85 86 87class Python3Supports(Describe, ABC):88 @classmethod89 def can_describe(cls, interpreter):90 return interpreter.version_info.major == 3 and super().can_describe(interpreter) # noqa: PLR200491 92 93class PosixSupports(Describe, ABC):94 @classmethod95 def can_describe(cls, interpreter):96 return interpreter.os == "posix" and super().can_describe(interpreter)97 98 99class WindowsSupports(Describe, ABC):100 @classmethod101 def can_describe(cls, interpreter):102 return interpreter.os == "nt" and super().can_describe(interpreter)103 104 105__all__ = [106 "Describe",107 "PosixSupports",108 "Python3Supports",109 "WindowsSupports",110]111 