pip_invoke.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. from __future__ import annotations
  2. import logging
  3. from contextlib import contextmanager
  4. from subprocess import Popen
  5. from typing import TYPE_CHECKING
  6. from virtualenv.seed.embed.base_embed import BaseEmbed
  7. from virtualenv.seed.wheels import Version, get_wheel, pip_wheel_env_run
  8. from virtualenv.util.subprocess import LogCmd
  9. if TYPE_CHECKING:
  10. from collections.abc import Generator
  11. from pathlib import Path
  12. from virtualenv.config.cli.parser import VirtualEnvOptions
  13. from virtualenv.create.creator import Creator
  14. LOGGER = logging.getLogger(__name__)
  15. class PipInvoke(BaseEmbed):
  16. def __init__(self, options: VirtualEnvOptions) -> None:
  17. super().__init__(options)
  18. def run(self, creator: Creator) -> None:
  19. if not self.enabled:
  20. return
  21. for_py_version = creator.interpreter.version_release_str
  22. with self.get_pip_install_cmd(creator.exe, for_py_version) as cmd:
  23. env = pip_wheel_env_run(self.extra_search_dir, self.app_data, self.env)
  24. self._execute(cmd, env)
  25. @staticmethod
  26. def _execute(cmd: list[str], env: dict[str, str]) -> Popen[bytes]:
  27. LOGGER.debug("pip seed by running: %s", LogCmd(cmd, env))
  28. process = Popen(cmd, env=env)
  29. process.communicate()
  30. if process.returncode != 0:
  31. msg = f"failed seed with code {process.returncode}"
  32. raise RuntimeError(msg)
  33. return process
  34. @contextmanager
  35. def get_pip_install_cmd(self, exe: Path, for_py_version: str) -> Generator[list[str], None, None]:
  36. cmd = [
  37. str(exe),
  38. "-m",
  39. "pip",
  40. "-q",
  41. "install",
  42. "--only-binary",
  43. ":all:",
  44. "--disable-pip-version-check",
  45. "--ignore-installed",
  46. ]
  47. if not self.download:
  48. cmd.append("--no-index")
  49. folders = set()
  50. for dist, version in self.distribution_to_versions().items():
  51. wheel = get_wheel(
  52. distribution=dist,
  53. version=version,
  54. for_py_version=for_py_version,
  55. search_dirs=self.extra_search_dir,
  56. download=False,
  57. app_data=self.app_data,
  58. do_periodic_update=self.periodic_update,
  59. env=self.env,
  60. )
  61. if wheel is None:
  62. msg = f"could not get wheel for distribution {dist}"
  63. raise RuntimeError(msg)
  64. folders.add(str(wheel.path.parent))
  65. cmd.append(Version.as_pip_req(dist, wheel.version))
  66. for folder in sorted(folders):
  67. cmd.extend(["--find-links", str(folder)])
  68. yield cmd
  69. __all__ = [
  70. "PipInvoke",
  71. ]