Skip to content

Instantly share code, notes, and snippets.

@guillermo-carrasco
Last active October 28, 2021 08:35
Show Gist options
  • Save guillermo-carrasco/009155650dd413b8807f159fded56cb7 to your computer and use it in GitHub Desktop.
Save guillermo-carrasco/009155650dd413b8807f159fded56cb7 to your computer and use it in GitHub Desktop.
Update all versions of a requirements.txt file to the latest ones (or just list the resulting file with -d)
import argparse
import logging
import re
import subprocess
import sys
from pathlib import Path
from typing import List
def fetch_latest_version(library):
# Calling pip install with a version that does not exist will trigger an error message that will list all available
# versions of the library. We can specify something that for sure does not exist in order to get that list
latest_version = str(
subprocess.run(
[sys.executable, "-m", "pip", "install", f"{library}==gibberish"], capture_output=True, text=True
)
)
versions_array_start = latest_version.find("(from versions: ") + len("(from versions: ")
versions_array_end = latest_version.find(")")
versions_array = latest_version[versions_array_start:versions_array_end].split(", ")
# Filter out release candidates, beta and alpha releases
versions_array_releases = [v for v in versions_array if not re.findall("[a-z]", v)]
# Some packages do not have a stable release at all. For these particular cases, choose the latest candidate
if not versions_array_releases:
return versions_array[-1]
else:
return versions_array_releases[-1]
def update_requirements(requirements_file: Path, skip_list: List[str], dry_run: bool):
print(skip_list)
with open(requirements_file, "r") as f:
requirements = f.readlines()
updated_requirements = []
for line in requirements:
if re.match("^[a-z]", line):
# Some libraries might not be pinned to a specific version, in such case just pin it to the latest version
if "==" in line:
library, version = line.strip().split("==")
else:
library = line.strip()
version = None
if library not in skip_list:
latest_version = fetch_latest_version(library)
logging.debug(
f"Library: {library}: Current pinned version is {version}, latest version is {latest_version}"
)
if version != latest_version:
logging.info(f"Updating {library} to {latest_version} in requirements file")
line = f"{library}=={latest_version}\n"
updated_requirements.append(line)
if dry_run:
logging.info("".join(updated_requirements))
else:
with open(requirements_file, "w") as f:
f.writelines(updated_requirements)
if __name__ == "__main__":
logging.getLogger().setLevel(logging.INFO)
parser = argparse.ArgumentParser(description="Update your requirements file to the latest versions")
parser.add_argument("requirements_file", type=Path)
parser.add_argument("--skip", type=str, default="", help="Comma-separated list of libraries to skip update for")
parser.add_argument(
"-d", "--dry-run", action="store_true", help="Do not actually update the file, but just output the result"
)
args = parser.parse_args()
skip_list = args.skip.split(",")
update_requirements(args.requirements_file, skip_list, args.dry_run)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment