Created
September 2, 2024 12:32
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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