如何用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
相关产品推荐
相关产品推荐

