Skip to content

Commit

Permalink
Speed up isolated environment creation
Browse files Browse the repository at this point in the history
Instead of creating a zip file from the current pip's sources, add
it to the build environment's interpreter import system using
`sys.meta_path`.
  • Loading branch information
pradyunsg committed Jul 14, 2022
1 parent c0fb4bf commit ea9cdf8
Showing 1 changed file with 31 additions and 13 deletions.
44 changes: 31 additions & 13 deletions src/pip/_internal/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import pathlib
import sys
import textwrap
import zipfile
from collections import OrderedDict
from sysconfig import get_paths
from types import TracebackType
Expand All @@ -29,6 +28,29 @@

logger = logging.getLogger(__name__)

PIP_RUNNER = """
import importlib.util
import os
import runpy
import sys
class PipImportRedirectingFinder:
@classmethod
def find_spec(cls, fullname, path=None, target=None):
if not fullname.startswith("pip."):
return None
# Import pip from the current source directory
location = os.path.join({source!r}, *fullname.split("."))
return importlib.util.spec_from_file_location(fullname, location)
sys.meta_path.append(PipImportRedirectingFinder())
runpy.run_module("pip", run_name="__main__")
"""


class _Prefix:
def __init__(self, path: str) -> None:
Expand All @@ -42,10 +64,10 @@ def __init__(self, path: str) -> None:


@contextlib.contextmanager
def _create_standalone_pip() -> Generator[str, None, None]:
"""Create a "standalone pip" zip file.
def _create_runnable_pip() -> Generator[str, None, None]:
"""Create a "pip runner" file.
The zip file's content is identical to the currently-running pip.
The runner file ensures that import for pip happen using the currently-running pip.
It will be used to install requirements into the build environment.
"""
source = pathlib.Path(pip_location).resolve().parent
Expand All @@ -57,14 +79,10 @@ def _create_standalone_pip() -> Generator[str, None, None]:
return

with TempDirectory(kind="standalone-pip") as tmp_dir:
pip_zip = os.path.join(tmp_dir.path, "__env_pip__.zip")
kwargs = {}
if sys.version_info >= (3, 8):
kwargs["strict_timestamps"] = False
with zipfile.ZipFile(pip_zip, "w", **kwargs) as zf:
for child in source.rglob("*"):
zf.write(child, child.relative_to(source.parent).as_posix())
yield os.path.join(pip_zip, "pip")
pip_runner = os.path.join(tmp_dir.path, "__pip-runner__.py")
with open(pip_runner, "w") as f:
f.write(PIP_RUNNER.format(source=os.fsdecode(source)))
yield pip_runner


class BuildEnvironment:
Expand Down Expand Up @@ -206,7 +224,7 @@ def install_requirements(
if not requirements:
return
with contextlib.ExitStack() as ctx:
pip_runnable = ctx.enter_context(_create_standalone_pip())
pip_runnable = ctx.enter_context(_create_runnable_pip())
self._install_requirements(
pip_runnable,
finder,
Expand Down

0 comments on commit ea9cdf8

Please sign in to comment.