Skip to content

Instantly share code, notes, and snippets.

@balakrishnangithub
Created September 2, 2024 12:32
Show Gist options
  • Save balakrishnangithub/35c4cf283de306e0f394cf6790e0c2e3 to your computer and use it in GitHub Desktop.
Save balakrishnangithub/35c4cf283de306e0f394cf6790e0c2e3 to your computer and use it in GitHub Desktop.
A flexible Python script for managing package updates, supporting patch/minor/major upgrades while excluding pre-releases and providing user-friendly output.
# Created with the help of Claude 3.5 Sonnet https://claude.ai/chat/a514bbd8-5535-4e92-bf4a-938541753061
# Tip: Use pipreqs to extract the relevant packages first, then create an environment using that file, and finally run the script from there.
# pip install packaging
# python pip_upgrade_wizard.py [patch|minor|major]
import subprocess
import json
import requests
from packaging import version
import sys
import argparse
def get_installed_packages():
result = subprocess.run([sys.executable, '-m', 'pip', 'list', '--format=json'], capture_output=True, text=True)
return json.loads(result.stdout)
def get_all_versions(package_name):
url = f"https://pypi.org/pypi/{package_name}/json"
response = requests.get(url)
if response.status_code == 200:
return list(response.json()['releases'].keys())
return []
def safe_parse_version(ver):
try:
return version.parse(ver)
except version.InvalidVersion:
return None
def is_stable_version(ver):
v = safe_parse_version(ver)
return v is not None and not v.is_prerelease and not v.is_devrelease
def get_latest_version_by_type(current, versions, update_type):
current_version = safe_parse_version(current)
if not current_version:
return None
valid_versions = []
for ver in versions:
v = safe_parse_version(ver)
if v is None or not is_stable_version(ver):
continue
if update_type == 'major' and v > current_version:
valid_versions.append(v)
elif update_type == 'minor' and v.major == current_version.major and v > current_version:
valid_versions.append(v)
elif update_type == 'patch' and v.major == current_version.major and v.minor == current_version.minor and v > current_version:
valid_versions.append(v)
return str(max(valid_versions)) if valid_versions else None
def filter_updates(packages, update_type):
filtered = []
for pkg in packages:
current = pkg['version']
all_versions = get_all_versions(pkg['name'])
latest_version = get_latest_version_by_type(current, all_versions, update_type)
if latest_version:
filtered.append({'name': pkg['name'], 'version': current, 'latest_version': latest_version})
return filtered
def display_table(packages):
if not packages:
return
headers = ['Package', 'Current Version', 'Latest Version']
max_lengths = [len(header) for header in headers]
for pkg in packages:
max_lengths[0] = max(max_lengths[0], len(pkg['name']))
max_lengths[1] = max(max_lengths[1], len(pkg['version']))
max_lengths[2] = max(max_lengths[2], len(pkg['latest_version']))
row_format = '| {:<' + str(max_lengths[0]) + '} | {:<' + str(max_lengths[1]) + '} | {:<' + str(
max_lengths[2]) + '} |'
separator = '+' + '-' * (max_lengths[0] + 2) + '+' + '-' * (max_lengths[1] + 2) + '+' + '-' * (
max_lengths[2] + 2) + '+'
print(separator)
print(row_format.format(*headers))
print(separator)
for pkg in packages:
print(row_format.format(pkg['name'], pkg['version'], pkg['latest_version']))
print(separator)
def update_packages(packages):
for pkg in packages:
subprocess.run([sys.executable, '-m', 'pip', 'install', '--upgrade', f"{pkg['name']}=={pkg['latest_version']}"])
print("Packages updated successfully.")
def main():
parser = argparse.ArgumentParser(description="Update Python packages based on version type.")
parser.add_argument('update_type', choices=['major', 'minor', 'patch'], help="Type of update to perform")
args = parser.parse_args()
installed = get_installed_packages()
updates = filter_updates(installed, args.update_type)
if not updates:
print(f"No {args.update_type} updates available.")
return
print(f"The following packages have {args.update_type} updates available:")
display_table(updates)
confirm = input("Do you want to update these packages? (yes/no): ").lower()
if confirm == 'yes':
update_packages(updates)
else:
print("Update cancelled.")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment