You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用pip uninstall自动移除setup.py中data_files内的文件?

解决pip uninstall无法自动移除data_files的问题

首先得明白为什么pip默认不会删掉这些文件:你把桌面文件和图标放到了系统级的共享目录(/usr/share/applications和/usr/share/pixmaps),这些路径可能被其他应用共用。pip为了避免误删不属于你的文件,不会自动追踪和删除data_files里的系统级路径文件。

要实现自动卸载,我们可以通过自定义安装/卸载脚本,把这些文件的安装记录下来,或者用Linux标准的XDG工具来管理桌面入口和图标,下面是两种可行的方案:

方案一:用XDG工具管理(推荐,符合Linux规范)

这个方法利用Linux桌面环境的标准工具xdg-desktop-menu和xdg-icon-resource来安装/卸载桌面文件和图标,不需要硬编码路径,兼容性更好。

步骤1:调整文件结构

把你的桌面文件和图标放到Python包的资源目录里,比如:

myapp/
├── __init__.py
└── resources/
    ├── myapp.desktop
    └── myapp.png

步骤2:修改setup.py

在setup.py里添加package_data来包含这些资源,同时自定义安装和卸载命令来调用XDG工具:

from setuptools import setup, Command
import subprocess
from pkg_resources import resource_filename

class PostInstall(Command):
    description = "Install desktop entry and icon using XDG tools"
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        # 安装桌面入口
        desktop_file = resource_filename("myapp", "resources/myapp.desktop")
        subprocess.run(
            ["xdg-desktop-menu", "install", "--novendor", desktop_file],
            check=True,
            capture_output=True
        )
        # 安装图标(这里用48x48尺寸,你可以根据需要调整)
        icon_file = resource_filename("myapp", "resources/myapp.png")
        subprocess.run(
            ["xdg-icon-resource", "install", "--size", "48", icon_file, "myapp"],
            check=True,
            capture_output=True
        )

class PreUninstall(Command):
    description = "Uninstall desktop entry and icon using XDG tools"
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        # 卸载桌面入口
        subprocess.run(
            ["xdg-desktop-menu", "uninstall", "--novendor", "myapp.desktop"],
            check=False,
            capture_output=True
        )
        # 卸载图标
        subprocess.run(
            ["xdg-icon-resource", "uninstall", "--size", "48", "myapp"],
            check=False,
            capture_output=True
        )

setup(
    name="myapp",
    version="1.0.0",
    packages=["myapp"],
    package_data={
        "myapp": ["resources/*.desktop", "resources/*.png"]
    },
    cmdclass={
        "install": PostInstall,
        "uninstall": PreUninstall
    },
    # 其他配置...
)

步骤3:安装和卸载

  • 安装:sudo pip install .(需要sudo因为是系统级安装,如果你用--user则不需要,XDG会自动安装到用户目录)
  • 卸载:sudo pip uninstall myapp,这时自定义的卸载脚本会自动调用XDG工具删除桌面文件和图标。

方案二:手动记录安装路径并删除

如果你不想依赖XDG工具,可以通过在安装时记录文件的实际路径,卸载时读取记录并删除:

步骤1:修改setup.py

from setuptools import setup, Command
import os
import shutil
from pkg_resources import get_distribution, resource_filename

class CustomInstall(Command):
    description = "Install app with desktop entry and icon"
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        # 先执行默认的安装流程
        self.do_egg_install()
        
        # 复制桌面文件和图标到系统路径
        desktop_src = resource_filename("myapp", "resources/myapp.desktop")
        desktop_dest = "/usr/share/applications/myapp.desktop"
        shutil.copy(desktop_src, desktop_dest)

        icon_src = resource_filename("myapp", "resources/myapp.png")
        icon_dest = "/usr/share/pixmaps/myapp.png"
        shutil.copy(icon_src, icon_dest)

        # 记录安装的文件路径到package目录下的清单文件
        pkg_dir = os.path.join(get_distribution("myapp").location, "myapp")
        manifest_file = os.path.join(pkg_dir, "installed_files.txt")
        with open(manifest_file, "w") as f:
            f.write(f"{desktop_dest}\n{icon_dest}\n")

class CustomUninstall(Command):
    description = "Uninstall app and remove desktop entry/icon"
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        # 读取清单文件并删除对应的文件
        try:
            pkg_dir = os.path.join(get_distribution("myapp").location, "myapp")
            manifest_file = os.path.join(pkg_dir, "installed_files.txt")
            if os.path.exists(manifest_file):
                with open(manifest_file, "r") as f:
                    files_to_remove = f.read().splitlines()
                for file_path in files_to_remove:
                    if os.path.exists(file_path):
                        os.remove(file_path)
                os.remove(manifest_file)
        except Exception as e:
            print(f"Warning: Failed to clean up some files: {e}")

setup(
    name="myapp",
    version="1.0.0",
    packages=["myapp"],
    package_data={
        "myapp": ["resources/*.desktop", "resources/*.png"]
    },
    cmdclass={
        "install": CustomInstall,
        "uninstall": CustomUninstall
    },
    # 其他配置...
)

注意事项

  • 这个方案硬编码了系统路径,可能在不同Linux发行版上有差异(比如有些可能用/usr/local/share),不如XDG方案通用。
  • 同样需要sudo权限来写入系统目录。

为什么不用原来的data_files?

因为data_files定义的系统级路径不在pip的追踪范围内,pip只会管理site-packages下的文件,所以这些外部文件不会被自动删除。上面的方案都是把资源先打包到Python包中,再通过脚本复制到目标路径,同时记录这些操作,确保卸载时能清理干净。

内容的提问来源于stack exchange,提问作者Bumsik Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:01:03