describe.py 3.3 KB

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