|
14 | 14 | """The piptool module imports pip requirements into Bazel rules."""
|
15 | 15 |
|
16 | 16 | import argparse
|
| 17 | +import atexit |
17 | 18 | import json
|
18 | 19 | import os
|
19 | 20 | import pkgutil
|
20 | 21 | import re
|
| 22 | +import shutil |
21 | 23 | import sys
|
22 | 24 | import tempfile
|
23 | 25 | import zipfile
|
24 | 26 |
|
25 |
| -# PIP erroneously emits an error when bundled as a PAR file. We |
26 |
| -# disable the version check to silence it. |
27 |
| -try: |
28 |
| - # Make sure we're using a suitable version of pip as a library. |
29 |
| - # Fallback on using it as a CLI. |
30 |
| - from pip._vendor import requests |
31 |
| - |
32 |
| - from pip import main as _pip_main |
33 |
| - def pip_main(argv): |
34 |
| - # Extract the certificates from the PAR following the example of get-pip.py |
35 |
| - # https://github.com/pypa/get-pip/blob/430ba37776ae2ad89/template.py#L164-L168 |
36 |
| - cert_path = os.path.join(tempfile.mkdtemp(), "cacert.pem") |
37 |
| - with open(cert_path, "wb") as cert: |
38 |
| - cert.write(pkgutil.get_data("pip._vendor.requests", "cacert.pem")) |
39 |
| - argv = ["--disable-pip-version-check", "--cert", cert_path] + argv |
40 |
| - return _pip_main(argv) |
| 27 | +# Note: We carefully import the following modules in a particular |
| 28 | +# order, since these modules modify the import path and machinery. |
| 29 | +import pkg_resources |
41 | 30 |
|
42 |
| -except: |
43 |
| - import subprocess |
44 | 31 |
|
45 |
| - def pip_main(argv): |
46 |
| - return subprocess.call(['pip'] + argv) |
| 32 | +def extract_packages(package_names): |
| 33 | + """Extract zipfile contents to disk and add to import path""" |
| 34 | + |
| 35 | + # Set a safe extraction dir |
| 36 | + extraction_tmpdir = tempfile.mkdtemp() |
| 37 | + atexit.register(lambda: shutil.rmtree( |
| 38 | + extraction_tmpdir, ignore_errors=True)) |
| 39 | + pkg_resources.set_extraction_path(extraction_tmpdir) |
| 40 | + |
| 41 | + # Extract each package to disk |
| 42 | + dirs_to_add = [] |
| 43 | + for package_name in package_names: |
| 44 | + req = pkg_resources.Requirement.parse(package_name) |
| 45 | + extraction_dir = pkg_resources.resource_filename(req, '') |
| 46 | + dirs_to_add.append(extraction_dir) |
| 47 | + |
| 48 | + # Add extracted directories to import path ahead of their zip file |
| 49 | + # counterparts. |
| 50 | + sys.path[0:0] = dirs_to_add |
| 51 | + existing_pythonpath = os.environ['PYTHONPATH'].split(':') |
| 52 | + os.environ['PYTHONPATH'] = ':'.join(dirs_to_add + existing_pythonpath) |
| 53 | + |
| 54 | + |
| 55 | +# Wheel, pip, and setuptools are much happier running from actual |
| 56 | +# files on disk, rather than entries in a zipfile. Extract zipfile |
| 57 | +# contents, add those contents to the path, then import them. |
| 58 | +extract_packages(['pip', 'setuptools', 'wheel']) |
| 59 | + |
| 60 | +# Defeat pip's attempt to mangle sys.path |
| 61 | +saved_sys_path = sys.path |
| 62 | +sys.path = sys.path[:] |
| 63 | +import pip |
| 64 | +sys.path = saved_sys_path |
| 65 | + |
| 66 | +import setuptools |
| 67 | +import wheel |
| 68 | + |
| 69 | +# Make sure we're using a suitable version of pip as a library. |
| 70 | +# Fallback on using it as a CLI. |
| 71 | +from pip._vendor import requests |
| 72 | + |
| 73 | +from pip import main as _pip_main |
| 74 | +def pip_main(argv): |
| 75 | + argv = ["--disable-pip-version-check"] + argv |
| 76 | + return _pip_main(argv) |
| 77 | + |
47 | 78 |
|
48 | 79 | # TODO(mattmoor): We can't easily depend on other libraries when
|
49 | 80 | # being invoked as a raw .py file. Once bundled, we should be able
|
|
0 commit comments