describe.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. from __future__ import annotations
  2. from abc import ABC
  3. from collections import OrderedDict
  4. from pathlib import Path
  5. from virtualenv.info import IS_WIN
  6. class Describe:
  7. """Given a host interpreter tell us information about what the created interpreter might look like."""
  8. suffix = ".exe" if IS_WIN else ""
  9. def __init__(self, dest, interpreter) -> None:
  10. self.interpreter = interpreter
  11. self.dest = dest
  12. self._stdlib = None
  13. self._stdlib_platform = None
  14. self._system_stdlib = None
  15. self._conf_vars = None
  16. @property
  17. def bin_dir(self):
  18. return self.script_dir
  19. @property
  20. def script_dir(self):
  21. return self.dest / self.interpreter.install_path("scripts")
  22. @property
  23. def purelib(self):
  24. return self.dest / self.interpreter.install_path("purelib")
  25. @property
  26. def platlib(self):
  27. return self.dest / self.interpreter.install_path("platlib")
  28. @property
  29. def libs(self):
  30. return list(OrderedDict(((self.platlib, None), (self.purelib, None))).keys())
  31. @property
  32. def stdlib(self):
  33. if self._stdlib is None:
  34. self._stdlib = Path(self.interpreter.sysconfig_path("stdlib", config_var=self._config_vars))
  35. return self._stdlib
  36. @property
  37. def stdlib_platform(self):
  38. if self._stdlib_platform is None:
  39. self._stdlib_platform = Path(self.interpreter.sysconfig_path("platstdlib", config_var=self._config_vars))
  40. return self._stdlib_platform
  41. @property
  42. def _config_vars(self):
  43. if self._conf_vars is None:
  44. self._conf_vars = self._calc_config_vars(self.dest)
  45. return self._conf_vars
  46. def _calc_config_vars(self, to):
  47. sys_vars = self.interpreter.sysconfig_vars
  48. return {k: (to if v is not None and v.startswith(self.interpreter.prefix) else v) for k, v in sys_vars.items()}
  49. @classmethod
  50. def can_describe(cls, interpreter): # noqa: ARG003
  51. """Knows means it knows how the output will look."""
  52. return True
  53. @property
  54. def env_name(self):
  55. return self.dest.parts[-1]
  56. @property
  57. def exe(self):
  58. return self.bin_dir / f"{self.exe_stem()}{self.suffix}"
  59. @classmethod
  60. def exe_stem(cls):
  61. """Executable name without suffix - there seems to be no standard way to get this without creating it."""
  62. raise NotImplementedError
  63. def script(self, name):
  64. return self.script_dir / f"{name}{self.suffix}"
  65. class Python3Supports(Describe, ABC):
  66. @classmethod
  67. def can_describe(cls, interpreter):
  68. return interpreter.version_info.major == 3 and super().can_describe(interpreter) # noqa: PLR2004
  69. class PosixSupports(Describe, ABC):
  70. @classmethod
  71. def can_describe(cls, interpreter):
  72. return interpreter.os == "posix" and super().can_describe(interpreter)
  73. class WindowsSupports(Describe, ABC):
  74. @classmethod
  75. def can_describe(cls, interpreter):
  76. return interpreter.os == "nt" and super().can_describe(interpreter)
  77. __all__ = [
  78. "Describe",
  79. "PosixSupports",
  80. "Python3Supports",
  81. "WindowsSupports",
  82. ]