pip是如何卸载你安装的第三方库的

使用pip uninstall 命令可以卸载掉你所安装的第三方库,所有与其相关的文件都将被pip整理出来展示并询问是否真的要删除,类似下面的提示

pip uninstall nox
Found existing installation: nox 2020.8.22
Uninstalling nox-2020.8.22:
  Would remove:
    d:\python\lib\site-packages\nox-2020.8.22.dist-info\*
    d:\python\lib\site-packages\nox\*
    d:\python\scripts\nox.exe
    d:\python\scripts\tox-to-nox.exe
Proceed (y/n)?

你是否好奇过,pip是如何知道一个库的相关文件所处位置的呢?

pkg_resources

pip底层依赖python原生模块pkg_resources,如果你对该模块足够熟悉,很多pip的功能,你自己就可以实现。

第三方模块有多种发行方式,不同的发行方式有自己独特的记录自身模块信息的方法。这里以whl安装文件为例,使用这种文件安装的第三方库会在site-packages里留下一个以dist-info为结尾的文件夹,在这个文件夹里,保留了该模块的许多信息。

这其中有一个名为RECORD的文本文件,里面存储的是这个模块的文件信息,包括文件的地址和sha256值,pip正是从这个文件了解到一个模块有哪些文件。

使用pkg_resources模块,你自己也可以后的这些信息

import pkg_resources
dist = pkg_resources.get_distribution('nox')
for path in dist.get_metadata_lines("RECORD"):
    print(path)

get_metadata_lines方法返回的文件目录远远多于卸载时所提示的目录,卸载提示时,只需要提示上层文件夹和个别文件即可,而RECORD记录的是该模块所有的文件,那么pip是如何做到的呢,查看源码,它最终使用一个函数来提取关键的上层目录和文件。

def compact(paths):
    # type: (Iterable[str]) -> Set[str]
    """Compact a path set to contain the minimal number of paths
    necessary to contain all paths in the set. If /a/path/ and
    /a/path/to/a/file.txt are both in the set, leave only the
    shorter path."""

    sep = os.path.sep
    short_paths = set()  # type: Set[str]
    for path in sorted(paths, key=len):
        should_skip = any(
            path.startswith(shortpath.rstrip("*")) and
            path[len(shortpath.rstrip("*").rstrip(sep))] == sep
            for shortpath in short_paths
        )
        if not should_skip:
            short_paths.add(path)
    return short_paths

算法非常的巧妙,首先根据目录长度从小到大排序,而后遍历排序后的目录,将上层目录放入到集合short_paths中,后续遍历到的目录如果以某个短目录开头,就说明是这个目录的某个文件,无需放入short_paths中,如此,最终得到的便是上层文件目录和个别的文件地址。

剩下的事情就简单了,执行删除操作就可以了。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案