diff --git a/noxfile.py b/noxfile.py index 5d5f426c564..9de795fe50f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -21,7 +21,7 @@ LOCATIONS = { "common-wheels": "tests/data/common_wheels", - "protected-pip": "tools/tox_pip.py", + "protected-pip": "tools/protected_pip.py", } REQUIREMENTS = { "docs": "docs/requirements.txt", diff --git a/tools/protected_pip.py b/tools/protected_pip.py new file mode 100644 index 00000000000..e25555948d8 --- /dev/null +++ b/tools/protected_pip.py @@ -0,0 +1,60 @@ +"""Maintain and use a protected copy of pip for use during development. + +The protected copy of pip can be used to manipulate the environment while keeping a +potentially-non-functional installation of in-development pip in the development virtual +environment. + +This allows for setting up the test environments and exercising the in-development code, +even when it is not functional-enough to install the packages for setting up the +environment that it is being used it. +""" + +import os +import shutil +import subprocess +import sys +from glob import glob +from typing import List + +VIRTUAL_ENV = os.environ["VIRTUAL_ENV"] +PROTECTED_PIP_DIR = os.path.join(VIRTUAL_ENV, "pip") + + +def _setup_protected_pip() -> None: + # This setup happens before any development version of pip is installed in this + # environment. So, at this point, the existing pip installation should be from a + # stable release and can be safely used to create the protected copy. + subprocess.check_call( + [ + sys.executable, + "-m", + "pip", + "--disable-pip-version-check", + "install", + "-t", + PROTECTED_PIP_DIR, + "pip", + ] + ) + # Make it impossible for pip (and other Python tooling) to discover this protected + # installation of pip using the metadata, by deleting the metadata. + shutil.rmtree(glob(os.path.join(PROTECTED_PIP_DIR, "pip-*.dist-info"))[0]) + + +def main(args: List[str]) -> None: + # If we don't have a protected pip, let's set it up. + if not os.path.exists(PROTECTED_PIP_DIR): + _setup_protected_pip() + + # Run Python, with the protected pip copy on PYTHONPATH. + old_PYTHONPATH_entries = os.environ.get("PYTHONPATH", "").split(os.pathsep) + new_PYTHONPATH = os.pathsep.join([PROTECTED_PIP_DIR] + old_PYTHONPATH_entries) + + subprocess.check_call( + [sys.executable, "-m", "pip"] + args, + env={"PYTHONPATH": new_PYTHONPATH}, + ) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/tools/tox_pip.py b/tools/tox_pip.py deleted file mode 100644 index 671518029a5..00000000000 --- a/tools/tox_pip.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -import shutil -import subprocess -import sys -from glob import glob -from typing import List - -VIRTUAL_ENV = os.environ["VIRTUAL_ENV"] -TOX_PIP_DIR = os.path.join(VIRTUAL_ENV, "pip") - - -def pip(args: List[str]) -> None: - # First things first, get a recent (stable) version of pip. - if not os.path.exists(TOX_PIP_DIR): - subprocess.check_call( - [ - sys.executable, - "-m", - "pip", - "--disable-pip-version-check", - "install", - "-t", - TOX_PIP_DIR, - "pip", - ] - ) - shutil.rmtree(glob(os.path.join(TOX_PIP_DIR, "pip-*.dist-info"))[0]) - # And use that version. - pypath_env = os.environ.get("PYTHONPATH") - pypath = pypath_env.split(os.pathsep) if pypath_env is not None else [] - pypath.insert(0, TOX_PIP_DIR) - os.environ["PYTHONPATH"] = os.pathsep.join(pypath) - subprocess.check_call([sys.executable, "-m", "pip"] + args) - - -if __name__ == "__main__": - pip(sys.argv[1:])