Python 一键更新电脑所有第三方依赖的脚本

https://github.com/ltoddy/Python-useful/tree/master/update

(这个脚本是新版 pip 的方式, pip>10)


Python 一键更新电脑所有第三方依赖的脚本
27 回复

pip list --format legacy | awk ‘{print $1}’ | xargs pip install --upgrade


我来写一个一键更新所有第三方依赖的脚本。这玩意儿挺实用的,特别是项目多的时候。

#!/usr/bin/env python3
"""
一键更新所有第三方依赖的脚本
用法: python update_deps.py [--upgrade-type TYPE]
"""

import subprocess
import sys
import json
from pathlib import Path
from typing import List, Dict, Optional
import argparse

class DependencyUpdater:
    def __init__(self, upgrade_type: str = "upgrade"):
        """
        初始化更新器
        
        Args:
            upgrade_type: 更新类型
                - "upgrade": 升级到最新版本
                - "upgrade-patch": 只升级小版本和补丁
                - "upgrade-minor": 只升级小版本
        """
        self.upgrade_type = upgrade_type
        self.results = []
        
    def get_installed_packages(self) -> List[Dict]:
        """获取当前环境安装的所有包"""
        try:
            result = subprocess.run(
                [sys.executable, "-m", "pip", "list", "--format=json"],
                capture_output=True,
                text=True,
                check=True
            )
            packages = json.loads(result.stdout)
            return packages
        except subprocess.CalledProcessError as e:
            print(f"获取已安装包失败: {e}")
            return []
        except json.JSONDecodeError:
            print("解析包列表失败")
            return []
    
    def check_for_updates(self, package: str) -> Optional[str]:
        """检查包是否有更新"""
        try:
            result = subprocess.run(
                [sys.executable, "-m", "pip", "index", "versions", package],
                capture_output=True,
                text=True,
                check=False
            )
            
            # 解析输出,获取最新版本
            for line in result.stdout.split('\n'):
                if 'Available versions:' in line:
                    versions = line.split(':')[1].strip().split(', ')
                    if versions:
                        return versions[0]  # 第一个是最新版本
            return None
        except Exception as e:
            print(f"检查 {package} 更新失败: {e}")
            return None
    
    def update_package(self, package_name: str, current_version: str) -> bool:
        """更新单个包"""
        print(f"\n正在更新 {package_name} ({current_version})...")
        
        try:
            # 根据更新类型构建命令
            if self.upgrade_type == "upgrade-patch":
                # 使用 ~= 操作符只更新小版本和补丁
                cmd = [sys.executable, "-m", "pip", "install", f"{package_name}~={current_version.split('.')[0]}"]
            elif self.upgrade_type == "upgrade-minor":
                # 使用 ~= 操作符只更新小版本
                major = current_version.split('.')[0]
                cmd = [sys.executable, "-m", "pip", "install", f"{package_name}~={major}"]
            else:
                # 完全升级到最新版本
                cmd = [sys.executable, "-m", "pip", "install", "--upgrade", package_name]
            
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=True
            )
            
            # 获取更新后的版本
            new_result = subprocess.run(
                [sys.executable, "-m", "pip", "show", package_name],
                capture_output=True,
                text=True,
                check=True
            )
            
            # 解析新版本
            new_version = None
            for line in new_result.stdout.split('\n'):
                if line.startswith('Version:'):
                    new_version = line.split(':')[1].strip()
                    break
            
            if new_version and new_version != current_version:
                print(f"✓ {package_name}: {current_version} -> {new_version}")
                self.results.append({
                    'package': package_name,
                    'old_version': current_version,
                    'new_version': new_version,
                    'success': True
                })
                return True
            else:
                print(f"✓ {package_name}: 已经是最新版本 ({current_version})")
                self.results.append({
                    'package': package_name,
                    'old_version': current_version,
                    'new_version': current_version,
                    'success': True,
                    'up_to_date': True
                })
                return True
                
        except subprocess.CalledProcessError as e:
            print(f"✗ 更新 {package_name} 失败: {e.stderr}")
            self.results.append({
                'package': package_name,
                'old_version': current_version,
                'new_version': None,
                'success': False,
                'error': str(e)
            })
            return False
    
    def update_all(self) -> Dict:
        """更新所有包"""
        print("正在获取已安装的包列表...")
        packages = self.get_installed_packages()
        
        if not packages:
            print("没有找到已安装的包")
            return {'total': 0, 'updated': 0, 'failed': 0}
        
        print(f"找到 {len(packages)} 个包,开始更新...")
        
        updated_count = 0
        failed_count = 0
        
        for pkg in packages:
            package_name = pkg.get('name')
            current_version = pkg.get('version')
            
            if not package_name or not current_version:
                continue
            
            # 跳过pip自身,避免更新过程中出现问题
            if package_name.lower() == 'pip':
                print(f"跳过 pip (建议单独更新)")
                continue
            
            if self.update_package(package_name, current_version):
                updated_count += 1
            else:
                failed_count += 1
        
        # 生成报告
        report = {
            'total': len(packages),
            'updated': updated_count,
            'failed': failed_count,
            'results': self.results
        }
        
        print(f"\n{'='*50}")
        print(f"更新完成!")
        print(f"总计: {len(packages)} 个包")
        print(f"成功更新: {updated_count} 个")
        print(f"更新失败: {failed_count} 个")
        
        # 显示更新详情
        updated_packages = [r for r in self.results if r.get('new_version') and 
                          r.get('old_version') != r.get('new_version')]
        
        if updated_packages:
            print("\n更新的包:")
            for pkg in updated_packages:
                print(f"  {pkg['package']}: {pkg['old_version']} -> {pkg['new_version']}")
        
        return report
    
    def save_report(self, report: Dict, filename: str = "update_report.json"):
        """保存更新报告"""
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        print(f"\n更新报告已保存到: {filename}")

def main():
    parser = argparse.ArgumentParser(description='一键更新Python第三方依赖')
    parser.add_argument(
        '--upgrade-type',
        choices=['upgrade', 'upgrade-patch', 'upgrade-minor'],
        default='upgrade',
        help='更新类型: upgrade(完全更新), upgrade-patch(只更新小版本和补丁), upgrade-minor(只更新小版本)'
    )
    parser.add_argument(
        '--save-report',
        action='store_true',
        help='保存更新报告到JSON文件'
    )
    
    args = parser.parse_args()
    
    print("Python依赖一键更新工具")
    print("=" * 50)
    
    updater = DependencyUpdater(args.upgrade_type)
    report = updater.update_all()
    
    if args.save_report:
        updater.save_report(report)

if __name__ == "__main__":
    main()

这个脚本有几个特点:

  1. 三种更新模式

    • --upgrade-type upgrade:完全更新到最新版(默认)
    • --upgrade-type upgrade-patch:只更新小版本和补丁(比如 1.2.3 -> 1.2.4)
    • --upgrade-type upgrade-minor:只更新小版本(比如 1.2.3 -> 1.3.0)
  2. 详细报告:显示每个包的更新情况,更新前后的版本对比

  3. 安全跳过:自动跳过pip自身,避免更新过程中出问题

  4. 可选报告保存:可以用--save-report把更新结果保存到JSON文件

用法

# 完全更新所有包
python update_deps.py

# 只更新小版本和补丁(更安全)
python update_deps.py --upgrade-type upgrade-patch

# 更新并保存报告
python update_deps.py --save-report

注意:更新前最好先备份requirements.txt,大版本更新可能破坏兼容性。

建议先在小项目上测试,没问题再用到生产环境。

厉害
不过也得考虑 windows 用户。

Powershell 不会用,google 了一下,然后参照一楼写了个适合 Windows 用户的:

pip3 list --format legacy | %{ $.Split(’ ')[0]; } | %{&pip3 install -U $}

不怕更新了依赖以后不能用了?

这又不是小孩过家家,怎么可能随便的 release.

看样子你是没碰到过坑啊,没有写好测试,我也就只敢升小版本号改动的库。

能不能用和 release 没有必然关系,可能别人 release 了某个已弃用的 api

哇,楼主竟然敢直接更新=-=会炸的。

谁敢一键跟新所有的依赖啊

我就问一句,你敢在生产环境用吗,敢用的都是真的猛士

我还真敢在生产环境用……

这怎么敢随便更新啊

勇士啊,居然敢全部更新到最高

mkdocs 有依賴更新了大版本,直接爆炸

不如学学怎么做 rpm

点开看了一眼 你在搞笑?

第三方为什么叫第三方?

npm 都不敢这么干…

能回滚吗 233

666 lz 怕不是没死过

升级所有库

sudo pip3 freeze --local | grep -v ‘^-e’ | cut -d = -f 1 | xargs -n1 sudo pip3 install --upgrade -i https://pypi.mirrors.ustc.edu.cn/

sudo pip2 freeze --local | grep -v ‘^-e’ | cut -d = -f 1 | xargs -n1 sudo pip2 install --upgrade -i https://pypi.mirrors.ustc.edu.cn/



for /F “delims===” %i in (‘pip3 freeze -l’) do pip3 install -U %i

for /F “delims===” %i in (‘pip2 freeze -l’) do pip2 install -U %i

for /F "delims= " %i in (‘pip list --outdated’) do pip install -U %i

来自 <https://stackoverflow.com/questions/2720014/upgrading-all-packages-with-pip>

全在 virtualenv 中写

升级最新版可能会出现问题,不敢随便升

不敢乱更新,都是用特定版本包,最重要是稳! 除非更新包功能特别好…

其实啊,你在公司,当你想要引入一个第三方的 lib 的时候,都要很小心,不能随便就引一个第三方 lib 的。
当然更新也不是随随便便的。
但是,谁让我现在还是在校大学生呢,想怎么折腾就怎么折腾。
(大学生,了不起???

真正的勇士啊!

回到顶部