Skip to content

Instantly share code, notes, and snippets.

@peterbe
Created September 21, 2023 15:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterbe/a2b158c39f1f835c0977c82befd94cdf to your computer and use it in GitHub Desktop.
Save peterbe/a2b158c39f1f835c0977c82befd94cdf to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
import sys
import argparse
import re
import subprocess
def main(requirements_in, interactive=False):
required = {}
with open(requirements_in) as f:
for line in f:
if "==" in line:
package, version = line.strip().split("==")
package = package.split("[")[0]
required[package] = version
res = subprocess.run(["pip", "list", "--outdated"], capture_output=True)
if res.returncode:
raise Exception(res.stderr)
lines = res.stdout.decode("utf-8").splitlines()
relevant = [line for line in lines if line.split()[0] in required]
longest_package_name = max([len(x.split()[0]) for x in relevant]) if relevant else 0
for line in relevant:
p, installed, possible, *_ = line.split()
if p in required:
print(
p.ljust(longest_package_name + 2),
"INSTALLED:",
installed.ljust(9),
"POSSIBLE:",
possible,
)
if interactive:
print("")
with open(requirements_in) as f:
content = f.read()
changes = 0
for line in relevant:
p, installed, possible, *_ = line.split()
if p not in required:
continue
rex = re.compile(
f"({re.escape(p)})==({re.escape(installed)})", re.IGNORECASE
)
if rex.findall(content):
prompt = input(f"Update {p} from {installed} to {possible}? [y/N/q] ")
if prompt.lower() == "q":
break
if prompt.lower() == "y":
def replacer(match):
name, _ = match.groups()
return f"{name}=={possible}"
content = rex.sub(replacer, content)
changes += 1
if changes:
with open(requirements_in, "w") as f:
f.write(content)
parser = argparse.ArgumentParser(
prog="Pip-Outdated",
description="Tells you want in your requirements.in file is outdated (and possible update it)",
)
parser.add_argument("filename", default="requirements.in", nargs="?")
parser.add_argument("-p", "--print-only", action="store_true")
if __name__ == "__main__":
args = parser.parse_args()
interactive = not args.print_only
sys.exit(main(args.filename, interactive=interactive))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment