Skip to content

Commit 5f2a858

Browse files
committed
Move __pip-runner__ script into a module
This makes it possible to eliminate the need for writing a copy of this script in a temporary directory for each isolated build environment.
1 parent e975318 commit 5f2a858

File tree

2 files changed

+53
-48
lines changed

2 files changed

+53
-48
lines changed

src/pip/__pip-runner__.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Execute exactly this copy of pip, within a different environment.
2+
3+
This file is named as it is, to ensure that this module can't be imported via
4+
an import statement.
5+
"""
6+
7+
import importlib.util
8+
import runpy
9+
import sys
10+
import types
11+
from importlib.machinery import ModuleSpec
12+
from os.path import dirname, join
13+
from typing import Optional, Sequence, Union
14+
15+
PIP_SOURCES_ROOT = dirname(dirname(dirname(__file__)))
16+
17+
18+
class PipImportRedirectingFinder:
19+
@classmethod
20+
def find_spec(
21+
self,
22+
fullname: str,
23+
path: Optional[Sequence[Union[bytes, str]]] = None,
24+
target: Optional[types.ModuleType] = None,
25+
) -> Optional[ModuleSpec]:
26+
if not fullname.startswith("pip."):
27+
return None
28+
29+
# Import pip from the source directory of this file
30+
location = join(PIP_SOURCES_ROOT, *fullname.split("."))
31+
return importlib.util.spec_from_file_location(fullname, location)
32+
33+
34+
sys.meta_path.insert(0, PipImportRedirectingFinder())
35+
36+
assert __name__ == "__main__", "Cannot run __pip-runner__.py as a non-main module"
37+
runpy.run_module("pip", run_name="__main__")

src/pip/_internal/build_env.py

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Build Environment used for isolation during sdist building
22
"""
33

4-
import contextlib
54
import logging
65
import os
76
import pathlib
@@ -10,7 +9,7 @@
109
from collections import OrderedDict
1110
from sysconfig import get_paths
1211
from types import TracebackType
13-
from typing import TYPE_CHECKING, Generator, Iterable, List, Optional, Set, Tuple, Type
12+
from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type
1413

1514
from pip._vendor.certifi import where
1615
from pip._vendor.packaging.requirements import Requirement
@@ -28,29 +27,6 @@
2827

2928
logger = logging.getLogger(__name__)
3029

31-
PIP_RUNNER = """
32-
import importlib.util
33-
import os
34-
import runpy
35-
import sys
36-
37-
38-
class PipImportRedirectingFinder:
39-
40-
@classmethod
41-
def find_spec(cls, fullname, path=None, target=None):
42-
if not fullname.startswith("pip."):
43-
return None
44-
45-
# Import pip from the current source directory
46-
location = os.path.join({source!r}, *fullname.split("."))
47-
return importlib.util.spec_from_file_location(fullname, location)
48-
49-
50-
sys.meta_path.insert(0, PipImportRedirectingFinder())
51-
runpy.run_module("pip", run_name="__main__")
52-
"""
53-
5430

5531
class _Prefix:
5632
def __init__(self, path: str) -> None:
@@ -63,26 +39,20 @@ def __init__(self, path: str) -> None:
6339
self.lib_dirs = get_prefixed_libs(path)
6440

6541

66-
@contextlib.contextmanager
67-
def _create_runnable_pip() -> Generator[str, None, None]:
68-
"""Create a "pip runner" file.
42+
def _get_runnable_pip() -> str:
43+
"""Get a file to pass to a Python executable, to run the currently-running pip.
6944
70-
The runner file ensures that import for pip happens using the currently-running pip.
71-
It will be used to install requirements into the build environment.
45+
This is used to run a pip subprocess, for installing requirements into the build
46+
environment.
7247
"""
7348
source = pathlib.Path(pip_location).resolve().parent
7449

75-
# Return the current instance if `source` is not a directory. It likely
76-
# means that this copy of pip is already standalone.
7750
if not source.is_dir():
78-
yield str(source)
79-
return
51+
# This would happen if someone is using pip from inside a zip file. In that
52+
# case, we can use that directly.
53+
return str(source)
8054

81-
with TempDirectory(kind="standalone-pip") as tmp_dir:
82-
pip_runner = os.path.join(tmp_dir.path, "__pip-runner__.py")
83-
with open(pip_runner, "w", encoding="utf8") as f:
84-
f.write(PIP_RUNNER.format(source=os.fsdecode(source)))
85-
yield pip_runner
55+
return os.fsdecode(source / "__pip-runner__.py")
8656

8757

8858
class BuildEnvironment:
@@ -223,15 +193,13 @@ def install_requirements(
223193
prefix.setup = True
224194
if not requirements:
225195
return
226-
with contextlib.ExitStack() as ctx:
227-
pip_runnable = ctx.enter_context(_create_runnable_pip())
228-
self._install_requirements(
229-
pip_runnable,
230-
finder,
231-
requirements,
232-
prefix,
233-
kind=kind,
234-
)
196+
self._install_requirements(
197+
_get_runnable_pip(),
198+
finder,
199+
requirements,
200+
prefix,
201+
kind=kind,
202+
)
235203

236204
@staticmethod
237205
def _install_requirements(

0 commit comments

Comments
 (0)