如何找到某个Python发行版提供的模块?

4 投票
2 回答
594 浏览
提问于 2025-04-17 09:42

我需要根据一个叫做requirements.txt的文件,列出一些Python包里包含的模块。首先,这些包需要被安装,这样我才能在本地查看它们。

看起来我可以用pip.req.parse_requirements这个工具,从requirements文件中获取包的列表。接下来,我该怎么找到这些包里提供的模块名称呢?

2 个回答

2

正如你所说,软件包的集合并不等同于它们所包含的模块,这就带来了一个问题:一个软件包的安装过程通常是这样的——下载、解压,然后运行一个叫做setup.py的文件,这个文件负责后续的安装步骤。

简单来说,即使你有一个Python软件包,你也无法在不运行setup.py的情况下知道它具体会做什么。虽然可能有一些约定,你可以提取很多信息并做出一些合理的猜测,但实际上,只有运行这个'setup.py'文件,才能看到它到底会把什么安装到site-packages文件夹里。因此,parse_requirements或者其他pip内部的工具对你来说并没有太大用处,除非你只是对软件包本身感兴趣。

所以,针对你的问题,我认为最好的解决方案是:

  1. 创建一个没有site packages的虚拟环境
  2. 使用pip -r requirements.txt来实际安装所有的软件包
  3. sys.path中查找.py、.pyc文件,并进入子文件夹寻找__init__.py?文件,以此来建立模块列表。
  4. 结束这个虚拟环境,继续你的工作。

第三步可能还有其他更好的方法,我不太确定。此外,你仍然有可能错过动态创建的模块或其他复杂情况,但这个方法应该能覆盖大部分模块。

编辑:

这里有一些代码,应该能处理除了zip文件以外的所有情况:

import sys, os

def walk_modules_os(root):
    def inner_walk(dir_path, mod_path):
        filelist = os.listdir(dir_path)
        pyfiles = set()
        dirs = []
        for name in filelist:
            if os.path.isdir(os.path.join(dir_path, name)):
                dirs.append(name)
            else:
                pre, ext = os.path.splitext(name)
                if ext in ('.py', '.pyc', '.pyo'):
                    pyfiles.add(pre)

        if len(mod_path):
            if '__init__' not in pyfiles:
                return
            pyfiles.remove('__init__')
            yield mod_path

        for pyfile in pyfiles:
            yield mod_path + (pyfile,)

        for directory in dirs:
            sub = os.path.join(dir_path, directory)
            for mod in inner_walk(sub, mod_path + (directory,)):
                yield mod

    root = os.path.realpath(root)
    if not os.path.isdir(root):
        return iter([])
    return iter(inner_walk(root, tuple()))

# you could collect as a set of tuples and do set subtraction, too
for path in sys.path:
    for mod in walk_modules_os(path):
        print mod 

编辑 2:

哇,真是的。GWW的想法更好,远比我的方案要好得多。

3

如果你的Python版本是2.3或更高,可以考虑使用内置的 pkgutil 模块。

比如说,

import sys, pkgutil
mods = set()

#You may not need this part if you don't care about the builtin modules
print sys.builtin_module_names
for m in sys.builtin_module_names:
    if m != '__main__':
        mods.add(m)
        #mods.add(m)


for loader, name, ispkg in pkgutil.walk_packages():
    if name.find('.') == -1:
        mods.add(name)

print mods

撰写回答