From ce866238fb1e4deb26ffb1de9373a05d766bbac3 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 14:53:03 +0000 Subject: [PATCH 1/8] Drop the no-op tests --- .github/workflows/tests.yml | 29 ----------------------------- noxfile.py | 6 ------ tests/__init__.py | 0 tests/test_basics.py | 2 -- 4 files changed, 37 deletions(-) delete mode 100644 .github/workflows/tests.yml delete mode 100644 tests/__init__.py delete mode 100644 tests/test_basics.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 80ac12af..00000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Tests - -on: - push: - pull_request: - -jobs: - tests: - name: ${{ matrix.os }} / ${{ matrix.python }} - runs-on: ${{ matrix.os }}-latest - strategy: - matrix: - os: [Ubuntu, Windows] - python: ['2.7', '3.7'] - steps: - - uses: actions/checkout@master - - - uses: actions/setup-python@v2 - - run: pip install nox - - - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python }} - - name: nox + Windows + Python 2.7 workaround - # This is in PATH, so nox resolves to it - but then subsequent steps fail - run: rm C:/ProgramData/Chocolatey/bin/python2.7.exe - if: matrix.os == 'Windows' && matrix.python == '2.7' - - - run: nox -s tests-${{ matrix.python }} diff --git a/noxfile.py b/noxfile.py index b2081f45..ed905397 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,9 +1,3 @@ import os import nox - - -@nox.session(python=['2.7', '3.7']) -def tests(session): - session.install('pytest') - session.run('pytest') diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/test_basics.py b/tests/test_basics.py deleted file mode 100644 index 7f2df179..00000000 --- a/tests/test_basics.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_basics(): - pass From 3a6a640fa2828189b320da68cb711f6103d09138 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 14:55:16 +0000 Subject: [PATCH 2/8] Drop existing invoke task --- tasks/__init__.py | 5 -- tasks/generate.py | 150 ---------------------------------------------- 2 files changed, 155 deletions(-) delete mode 100644 tasks/__init__.py delete mode 100644 tasks/generate.py diff --git a/tasks/__init__.py b/tasks/__init__.py deleted file mode 100644 index 250fe9c4..00000000 --- a/tasks/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import invoke - -from . import generate - -ns = invoke.Collection(generate) diff --git a/tasks/generate.py b/tasks/generate.py deleted file mode 100644 index 0933821c..00000000 --- a/tasks/generate.py +++ /dev/null @@ -1,150 +0,0 @@ -import base64 -import hashlib -import io -import json -import os -import os.path -import re -import zipfile - -import urllib.request - -import invoke -import packaging.specifiers -import packaging.version - - -PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - - -def _path(pyversion=None): - parts = [PROJECT_ROOT, pyversion, "get-pip.py"] - return os.path.join(*filter(None, parts)) - - -def _template(name="default.py"): - return os.path.join(PROJECT_ROOT, "templates", name) - - -@invoke.task -def installer(ctx, - pip_version=None, wheel_version=None, setuptools_version=None, - installer_path=_path(), template_path=_template()): - - print("[generate.installer] Generating installer {} (using {})".format( - os.path.relpath(installer_path, PROJECT_ROOT), - "pip" + pip_version if pip_version is not None else "latest" - )) - - # Load our wrapper template - with open(template_path, "r", encoding="utf8") as fp: - WRAPPER_TEMPLATE = fp.read() - - # Get all of the versions on PyPI - resp = urllib.request.urlopen("https://pypi.python.org/pypi/pip/json") - data = json.loads(resp.read().decode("utf8")) - versions = sorted(data["releases"].keys(), key=packaging.version.parse) - - # Filter our list of versions based on the given specifier - s = packaging.specifiers.SpecifierSet( - "" if pip_version is None else pip_version) - versions = list(s.filter(versions)) - - # Select the latest version that matches our specifier is - latest = versions[-1] - - # Select the wheel file (we assume there will be only one per release) - file_urls = [ - (x["url"], x["md5_digest"]) - for x in data["releases"][latest] - if x["url"].endswith(".whl") - ] - assert len(file_urls) == 1 - url, expected_hash = file_urls[0] - - # Fetch the file itself. - data = urllib.request.urlopen(url).read() - assert hashlib.md5(data).hexdigest() == expected_hash - - # We need to repack the downloaded wheel file to remove the .dist-info, - # after this it will no longer be a valid wheel, but it will still work - # perfectly fine for our use cases. - new_data = io.BytesIO() - with zipfile.ZipFile(io.BytesIO(data)) as existing_zip: - with zipfile.ZipFile(new_data, mode="w") as new_zip: - for zinfo in existing_zip.infolist(): - if re.search(r"pip-.+\.dist-info/", zinfo.filename): - continue - new_zip.writestr(zinfo, existing_zip.read(zinfo)) - data = new_data.getvalue() - - # Write out the wrapper script that will take the place of the zip script - # The reason we need to do this instead of just directly executing the - # zip script is that while Python will happily execute a zip script if - # passed it on the file system, it will not however allow this to work if - # passed it via stdin. This means that this wrapper script is required to - # make ``curl https://...../get-pip.py | python`` continue to work. - print( - "[generate.installer] Write the wrapper script with the bundled zip " - "file" - ) - - zipdata = base64.b85encode(data).decode("utf8") - chunked = [] - - for i in range(0, len(zipdata), 79): - chunked.append(zipdata[i:i + 79]) - - os.makedirs(os.path.dirname(installer_path), exist_ok=True) - with open(installer_path, "w") as fp: - fp.write( - WRAPPER_TEMPLATE.format( - pip_version="" if pip_version is None else pip_version, - wheel_version="" if wheel_version is None else wheel_version, - setuptools_version=( - "" if setuptools_version is None else setuptools_version), - installed_version=latest, - zipfile="\n".join(chunked), - ), - ) - - # Ensure the permissions on the newly created file - oldmode = os.stat(installer_path).st_mode & 0o7777 - newmode = (oldmode | 0o555) & 0o7777 - os.chmod(installer_path, newmode) - - print("[generate.installer] Generated installer") - - -@invoke.task( - default=True, - pre=[ - invoke.call(installer), - invoke.call(installer, installer_path=_path("2.6"), - template_path=_template("pre-10.py"), - pip_version="<10", - wheel_version="<0.30", - setuptools_version="<37"), - invoke.call(installer, installer_path=_path("2.7"), - template_path=_template("pre-21.0.py"), - pip_version="<21.0", - setuptools_version="<45"), - invoke.call(installer, installer_path=_path("3.2"), - template_path=_template("pre-10.py"), - pip_version="<8", - wheel_version="<0.30", - setuptools_version="<30"), - invoke.call(installer, installer_path=_path("3.3"), - template_path=_template("pre-18.1.py"), - pip_version="<18", - wheel_version="<0.30"), - invoke.call(installer, installer_path=_path("3.4"), - template_path=_template("pre-19.3.py"), - pip_version="<19.2"), - invoke.call(installer, installer_path=_path("3.5"), - template_path=_template("pre-21.0.py"), - pip_version="<21.0"), - ], -) -def all(ctx): - pass From eae977387fd546843ec4654170c7250c8aa1f243 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 14:55:28 +0000 Subject: [PATCH 3/8] Drop requirements.txt --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 0c13a44b..00000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -invoke -packaging From bb9b4f45a01c210f51230cda8827f21fa4cab29c Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 14:58:07 +0000 Subject: [PATCH 4/8] Add "scripts/generate.py" and setup nox session This reintroduces the logic we had for generating our get-pip.py scripts with 3 major changes: - drop dependency on invoke. - add caching using requests and cachecontrol. - setup a nox session to orchestrate everything. --- .gitignore | 1 + noxfile.py | 9 ++ scripts/generate.py | 197 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 scripts/generate.py diff --git a/.gitignore b/.gitignore index 23276e9b..5ef39967 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.nox/ /venv/ __pycache__/ +.web_cache/ diff --git a/noxfile.py b/noxfile.py index ed905397..180c1f02 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,3 +1,12 @@ import os +from pathlib import Path import nox + + +@nox.session +def generate(session): + """Update the scripts, to the latest versions.""" + session.install("packaging", "requests", "cachecontrol[filecache]") + + session.run("python", "scripts/generate.py") diff --git a/scripts/generate.py b/scripts/generate.py new file mode 100644 index 00000000..e7e142e1 --- /dev/null +++ b/scripts/generate.py @@ -0,0 +1,197 @@ +"""Update all the get-pip.py scripts.""" + +import hashlib +import operator +import re +import subprocess +import sys +from base64 import b85encode +from functools import lru_cache +from io import BytesIO +from pathlib import Path, PosixPath +from typing import Dict, Iterable, List, Tuple +from zipfile import ZipFile + +import requests +from cachecontrol import CacheControl +from cachecontrol.caches.file_cache import FileCache +from packaging.specifiers import SpecifierSet +from packaging.version import Version + +SCRIPT_CONSTRAINTS = { + "default": { + "pip": "", + "setuptools": "", + "wheel": "", + }, + "2.6": { + "pip": "<10", + "setuptools": "<37", + "wheel": "<0.30", + }, + "2.7": { + "pip": "<21.0", + "setuptools": "<45", + "wheel": "", + }, + "3.2": { + "pip": "<8", + "setuptools": "<30", + "wheel": "<0.30", + }, + "3.3": { + "pip": "<18", + "setuptools": "", + "wheel": "<0.30", + }, + "3.4": { + "pip": "<19.2", + "setuptools": "", + "wheel": "", + }, + "3.5": { + "pip": "<21.0", + "setuptools": "", + "wheel": "", + }, +} + + +def get_all_pip_versions() -> Dict[Version, Tuple[str, str]]: + data = requests.get("https://pypi.python.org/pypi/pip/json").json() + + versions = sorted(Version(s) for s in data["releases"].keys()) + + retval = {} + for version in versions: + wheels = [ + (file["url"], file["md5_digest"]) + for file in data["releases"][str(version)] + if file["url"].endswith(".whl") + ] + if not wheels: + continue + assert len(wheels) == 1, (version, wheels) + retval[version] = wheels[0] + return retval + + +def determine_latest(versions: Iterable[Version], *, constraint: str): + assert sorted(versions) == list(versions) + return list(SpecifierSet(constraint).filter(versions))[-1] + + +@lru_cache +def get_ordered_templates() -> List[Tuple[Version, Path]]: + all_templates = list(Path("./templates").iterdir()) + + fallback = None + ordered_templates = [] + for template in all_templates: + if template.name == "default.py": + fallback = template + continue + assert template.name.startswith("pre-") + + version_str = template.name[4:-3] # "pre-{version}.py" + version = Version(version_str) + ordered_templates.append((version, template)) + + # Use the epoch mechanism, to force the fallback to the end. + assert fallback is not None + ordered_templates.append((Version("1!0"), fallback)) + + # Order the (version, template) tuples, by increasing version numbers. + return sorted(ordered_templates, key=operator.itemgetter(0)) + + +def determine_template(version: Version): + ordered_templates = get_ordered_templates() + for template_version, template in ordered_templates: + if version < template_version: + return template + else: + assert template.name == "default.py" + return template + + +def download_wheel(url: str, expected_md5: str) -> bytes: + session = requests.session() + cached_session = CacheControl(session, cache=FileCache(".web_cache")) + + response = cached_session.get(url) + return response.content + + +def repack_wheel(data: bytes): + """Remove the .dist-info, so that this is no longer a valid wheel.""" + new_data = BytesIO() + with ZipFile(BytesIO(data)) as existing_zip: + with ZipFile(new_data, mode="w") as new_zip: + for zipinfo in existing_zip.infolist(): + if re.search(r"pip-.+\.dist-info/", zipinfo.filename): + continue + new_zip.writestr(zipinfo, existing_zip.read(zipinfo)) + + return new_data.getvalue() + + +def encode_wheel_contents(data: bytes) -> str: + zipdata = b85encode(data).decode("utf8") + + chunked = [] + for i in range(0, len(zipdata), 79): + chunked.append(zipdata[i : i + 79]) + + return "\n".join(chunked) + + +def determine_destination(version: str) -> Path: + public = Path(".") + if not public.exists(): + public.mkdir() + + if version == "default": + return public / "get-pip.py" + + retval = public / version / "get-pip.py" + if not retval.parent.exists(): + retval.parent.mkdir() + + return retval + + +def main() -> None: + print("Fetch available pip versions...") + pip_versions = get_all_pip_versions() + + for version, mapping in SCRIPT_CONSTRAINTS.items(): + print(f"Working on {version}") + destination = determine_destination(version) + pip_version = determine_latest( + pip_versions.keys(), + constraint=mapping["pip"], + ) + template = determine_template(pip_version) + + wheel_url, wheel_hash = pip_versions[pip_version] + print(f" Downloading {PosixPath(wheel_url).name}") + original_wheel = download_wheel(wheel_url, wheel_hash) + repacked_wheel = repack_wheel(original_wheel) + encoded_wheel = encode_wheel_contents(repacked_wheel) + + print(f" Generating with {template}") + rendered_template = template.read_text().format( + zipfile=encoded_wheel, + installed_version=pip_version, + pip_version=mapping["pip"], + setuptools_version=mapping["setuptools"], + wheel_version=mapping["wheel"], + ) + + print(f" Writing to {destination}") + destination.write_text(rendered_template) + + +if __name__ == "__main__": + main() From 42fde79a60c320207f5b19bc76aa61ec6aee7cec Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 15:04:27 +0000 Subject: [PATCH 5/8] Add CI to check scripts are generated correctly --- .github/workflows/check.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/check.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000..e8d32df2 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,18 @@ +name: Check Scripts + +on: + push: + pull_request: + +jobs: + correctly-generated: + name: "are correctly generated" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - run: pip install nox + + - run: nox -s generate + - name: Check regenerated scripts vs what is generated by automation. + run: git diff --exit-code From a523874027280c79ccc9cd026f68e0e4800074b6 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 15:30:31 +0000 Subject: [PATCH 6/8] Add a nox session to run after pip release This handles all the git interactions as well, simplifying the process for whoever makes a pip release. --- noxfile.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/noxfile.py b/noxfile.py index 180c1f02..23df9483 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,8 +1,11 @@ import os +import textwrap from pathlib import Path import nox +nox.options.sessions = ["check", "generate"] + @nox.session def generate(session): @@ -10,3 +13,54 @@ def generate(session): session.install("packaging", "requests", "cachecontrol[filecache]") session.run("python", "scripts/generate.py") + + +@nox.session(name="update-for-release") +def update_for_release(session): + """Automation to run after a pip release.""" + allowed_upstreams = [ + "git@github.com:pypa/get-pip.git", + "https://github.com/pypa/get-pip.git", + ] + + if len(session.posargs) != 1: + session.error("Usage: nox -s update-for-release -- ") + + release_version, = session.posargs + + session.install("release-helper") + session.run("release-helper", "version-check-validity", release_version) + session.run("release-helper", "git-check-tag", release_version, "--does-not-exist") + session.run("release-helper", "git-check-remote", "upstream", *allowed_upstreams) + session.run("release-helper", "git-check-branch", "master") + session.run("release-helper", "git-check-clean") + + # Generate the scripts. + generate(session) + + # Make the commit and present it to the user. + session.run("git", "add", ".", external=True) + session.run( + "git", "commit", "-m", f"Update to {release_version}", external=True + ) + session.run("git", "show", "HEAD", "--stat") + + input(textwrap.dedent( + """\ + ********************************************** + * IMPORTANT: Check which files got modified. * + ********************************************** + + Press enter to continue. This script will generate a signed tag for this + commit and push it -- which will publish these changes. + """ + )) + + session.run( + # fmt: off + "git", "tag", release_version, "-m", f"Release {release_version}", + "--annotate", "--sign", + external=True, + # fmt: on + ) + session.run("git", "push", "upstream", "master", release_version, external=True) From 2f1c9cc3a4989705be996b141f39aec5bd1cf910 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 15:36:45 +0000 Subject: [PATCH 7/8] Update README, and translate to Markdown --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++ README.rst | 86 ------------------------------------------------------ 2 files changed, 72 insertions(+), 86 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 00000000..58a87c0a --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# get-pip.py + +`get-pip.py` is a bootstrapping script that enables users to install pip, +setuptools, and wheel in Python environments that don't already have them. You +should not directly reference the files located in this repository and instead +use the versions located at . + +## Usage + +```console +$ curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py +$ python get-pip.py +``` + +Upon execution, `get-pip.py` will install `pip`, `setuptools` and `wheel` in +the current Python environment. + +It is possible to provide additional arguments to the underlying script. These +are passed through to the underlying `pip install` command, and can thus be +used to constraint the versions of the packages, or to pass other pip options +such as `--no-index`. + +```console +$ python get-pip.py "pip < 21.0" "setuptools < 50.0" "wheel < 1.0" +$ python get-pip.py --no-index --find-links=/local/copies +``` + +### get-pip.py options + +This script also has it's own options, which control which packages it will +install. + +- `--no-setuptools`: do not attempt to install `setuptools`. +- `--no-wheel`: do not attempt to install `wheel`. + +## Development + +You need to have a [`nox`](https://nox.readthedocs.io/) available on the CLI. + +### How it works + +`get-pip.py` bundles a copy of pip with a tiny amount of glue code. This glue +code comes from the `templates/` directory. + +### Updating after a pip release + +If you just made a pip release, run `nox -s update-for-release -- `. +This session will handle all the script updates (by running `generate`), commit +the changes and tag the commit. + +IMPORTANT: Check that the correct files got modified before pushing. The session +will pause to let you do this. + +### Generating the scripts + +Run `nox -s generate`. + +## Discussion + +If you run into bugs, you can file them in our [issue tracker]. + +You can also join `#pypa` or `#pypa-dev` on Freenode to ask questions or +get involved. + +[issue tracker]: https://github.com/pypa/get-pip/issues + +## Code of Conduct + +Everyone interacting in the get-pip project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the [PSF Code of Conduct]. + +[PSF Code of Conduct]: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md diff --git a/README.rst b/README.rst deleted file mode 100644 index d58210bc..00000000 --- a/README.rst +++ /dev/null @@ -1,86 +0,0 @@ -get-pip.py -========== - -``get-pip.py`` is a bootstrapping script that enables users to install pip, -setuptools, and wheel in Python environments that don't already have them. You -should not directly reference the files located in this repository and instead -use the versions located at https://bootstrap.pypa.io/. - - -Usage ------ - -.. code-block:: console - - $ curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py - $ python get-pip.py - -Upon execution, ``get-pip.py`` will install ``pip``, ``setuptools`` and -``wheel`` in the current Python environment. - -It is possible to provide additional arguments to the underlying script. These -are passed through to the underlying `pip install` command, and can thus be -used to constraint the versions of the packages, or to pass other pip options -such as ``--no-index``. - -.. code-block:: console - - $ python get-pip.py "pip < 21.0" "setuptools < 50.0" "wheel < 1.0" - $ python get-pip.py --no-index --find-links=/local/copies - -get-pip.py options -^^^^^^^^^^^^^^^^^^ - -This script also has it's own options, which control which packages it will -install. - -``--no-setuptools`` - If set, do not attempt to install ``setuptools``. - -``--no-wheel`` - If set, do not attempt to install ``wheel``. - - -Development ------------ - -Most of the work of ``get-pip.py`` comes from the copy of pip that is bundled -inside of it. However, there is a tiny bit of glue code located inside of -``template.py`` to enable ``get-pip.py`` to actually work. - -Install the dependencies from ``requirements.txt`` to get setup with the repo. -You may want to perform something like: - -.. code-block:: console - - $ python3 -m venv venv - $ source venv/bin/activate - $ pip install -r requirements.txt - -Any pull request should include regenerated files, which can be generated by -running: - -.. code-block:: console - - $ invoke generate - - -Discussion ----------- - -If you run into bugs, you can file them in our `issue tracker`_. - -You can also join ``#pypa`` or ``#pypa-dev`` on Freenode to ask questions or -get involved. - - -.. _`issue tracker`: https://github.com/pypa/get-pip/issues - - -Code of Conduct ---------------- - -Everyone interacting in the get-pip project's codebases, issue trackers, chat -rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. - -.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md From 726ff7bf111114a12a7b31cba8b549e100a7b5d4 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 24 Jan 2021 15:27:05 +0000 Subject: [PATCH 8/8] Add CI to check that these scripts install pip --- .github/workflows/check.yml | 31 +++++++++++++++++++++++++++++++ noxfile.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e8d32df2..27745a45 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -16,3 +16,34 @@ jobs: - run: nox -s generate - name: Check regenerated scripts vs what is generated by automation. run: git diff --exit-code + + work-as-advertised: + name: "work as advertised" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - run: pip install nox + + # Install supported Python versions + - uses: actions/setup-python@v2 + with: + python-version: 2.7 + - uses: actions/setup-python@v2 + with: + python-version: 3.5 + - uses: actions/setup-python@v2 + with: + python-version: 3.6 + - uses: actions/setup-python@v2 + with: + python-version: 3.7 + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + - uses: actions/setup-python@v2 + with: + python-version: 3.9 + + # Check that the scripts work. + - run: nox -s check diff --git a/noxfile.py b/noxfile.py index 23df9483..a542f74d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -7,6 +7,34 @@ nox.options.sessions = ["check", "generate"] +# Keep versions in sync with .github/workflows/check.yml +@nox.session( + python=["2.6", "2.7", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9"] +) +def check(session): + """Ensure that get-pip.py for various Python versions, works on that version.""" + + # Find the appropriate get-pip.py file + public = Path(".") + locations = [ + public / session.python / "get-pip.py", + public / "get-pip.py", + ] + for location in locations: + if location.exists(): + break + else: # AKA nobreak + raise RuntimeError("There is no public get-pip.py") + + # Get rid of provided-by-nox pip + session.run("python", "-m", "pip", "uninstall", "pip", "--yes") + # Run the get-pip.py file + session.run("python", str(location)) + # Ensure that pip is installed + session.run("python", "-m", "pip", "--version") + session.run("pip", "--version") + + @nox.session def generate(session): """Update the scripts, to the latest versions."""