7
7
import pathlib
8
8
import sys
9
9
import textwrap
10
- import zipfile
11
10
from collections import OrderedDict
12
11
from sysconfig import get_paths
13
12
from types import TracebackType
29
28
30
29
logger = logging .getLogger (__name__ )
31
30
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
+
32
54
33
55
class _Prefix :
34
56
def __init__ (self , path : str ) -> None :
@@ -42,29 +64,25 @@ def __init__(self, path: str) -> None:
42
64
43
65
44
66
@contextlib .contextmanager
45
- def _create_standalone_pip () -> Generator [str , None , None ]:
46
- """Create a "standalone pip" zip file.
67
+ def _create_runnable_pip () -> Generator [str , None , None ]:
68
+ """Create a "pip runner" file.
47
69
48
- The zip file's content is identical to the currently-running pip.
70
+ The runner file ensures that import for pip happen using the currently-running pip.
49
71
It will be used to install requirements into the build environment.
50
72
"""
51
73
source = pathlib .Path (pip_location ).resolve ().parent
52
74
53
- # Return the current instance if `source` is not a directory. We can't build
54
- # a zip from this, and it likely means the instance is already standalone.
75
+ # Return the current instance if `source` is not a directory. It likely
76
+ # means that this executable is already standalone.
55
77
if not source .is_dir ():
56
78
yield str (source )
57
79
return
58
80
59
81
with TempDirectory (kind = "standalone-pip" ) as tmp_dir :
60
- pip_zip = os .path .join (tmp_dir .path , "__env_pip__.zip" )
61
- kwargs = {}
62
- if sys .version_info >= (3 , 8 ):
63
- kwargs ["strict_timestamps" ] = False
64
- with zipfile .ZipFile (pip_zip , "w" , ** kwargs ) as zf :
65
- for child in source .rglob ("*" ):
66
- zf .write (child , child .relative_to (source .parent ).as_posix ())
67
- yield os .path .join (pip_zip , "pip" )
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
68
86
69
87
70
88
class BuildEnvironment :
@@ -206,7 +224,7 @@ def install_requirements(
206
224
if not requirements :
207
225
return
208
226
with contextlib .ExitStack () as ctx :
209
- pip_runnable = ctx .enter_context (_create_standalone_pip ())
227
+ pip_runnable = ctx .enter_context (_create_runnable_pip ())
210
228
self ._install_requirements (
211
229
pip_runnable ,
212
230
finder ,
0 commit comments