如何获取所有Python标准库模块的列表?

58

我想要类似于 sys.builtin_module_names 的东西,但是针对的是标准库。其他没有起作用的方法有:

  • sys.modules - 只显示已加载的模块
  • sys.prefix - 路径会包括非标准库模块,而且在虚拟环境中似乎不起作用。

我希望得到这个列表是为了将其传递给 trace 命令行选项的 --ignore-module--ignore-dir

所以最终,我想知道如何在使用 tracesys.settrace 时忽略所有标准库模块。

11个回答

35

我通过编写一些代码来刮取Python官方文档中标准库页面的目录,进行了强制实现。我还建立了一个简单的API,用于获取标准库列表(适用于Python版本2.6、2.7、3.2、3.3和3.4)。

该软件包在这里,使用起来相当简单:

>>> from stdlib_list import stdlib_list
>>> libraries = stdlib_list("2.7")
>>> libraries[:10]
['AL', 'BaseHTTPServer', 'Bastion', 'CGIHTTPServer', 'ColorPicker', 'ConfigParser', 'Cookie', 'DEVICE', 'DocXMLRPCServer', 'EasyDialogs']

21

Python >= 3.10:

sys.stdlib_module_names

Python < 3.10:

isort 的作者曾为解决 PEP8 要求,在第三方库导入之前先导入 Python 核心库而苦恼。我使用了这个工具,效果不错。你可以在 isort.py 文件中使用方法 place_module

>>> from isort import place_module
>>> place_module("json")
'STDLIB'
>>> place_module("requests")
'THIRDPARTY'

或者您可以直接获取一组模块名称,这取决于Python版本,例如:

>>> from isort.stdlibs.py39 import stdlib
>>> for name in sorted(stdlib): print(name)
... <200+ lines>
xml
xmlrpc
zipapp
zipfile
zipimport
zlib
zoneinfo

不知怎么的,sys.stdlib_module_names 仍然缺少像 pkg_resources 这样的东西。 - xjcl
请注意sys.stdlib_module_names不会包括setuptools和相关的pkg_resources。这是因为尽管在venvs中预安装,但从技术上来说它并不属于标准库的一部分。 - xjcl
@xjcl stdlib_module_names 没有理由包含 setuptools/pkg_resources。这些不是标准库,并且从 Python 3.12 开始,也将不再被预装到 venvs 中。 - wim
只是试图增加对可能感到惊讶的用户的清晰度。 - xjcl

19

为什么不自己找出哪些是标准库的一部分呢?

import distutils.sysconfig as sysconfig
import os
std_lib = sysconfig.get_python_lib(standard_lib=True)
for top, dirs, files in os.walk(std_lib):
    for nm in files:
        if nm != '__init__.py' and nm[-3:] == '.py':
            print os.path.join(top, nm)[len(std_lib)+1:-3].replace(os.sep, '.')

提供

abc
aifc
antigravity
--- a bunch of other files ----
xml.parsers.expat
xml.sax.expatreader
xml.sax.handler
xml.sax.saxutils
xml.sax.xmlreader
xml.sax._exceptions

编辑:如果你需要避免使用非标准库模块,你可能需要添加一个检查以避免site-packages


使用 sys.real_prefix 可以在虚拟环境中工作。但我希望它既可以在虚拟环境内部,也可以在外部工作。我想我只需要一些逻辑来决定使用哪个前缀就行了。 - saltycrane
我不知道昨晚我在吸什么,但我再试了一次,sysconfig.get_python_lib(standard_lib=True) 即使在使用 virtualenv 时也适用于我。 - saltycrane
3
如果在我激活虚拟环境的情况下,当前工作目录为 /usr/lib/python2.6,当我调用 Python 解释器时,执行 sysconfig.get_python_lib(standard_lib=True) 命令会返回我的虚拟环境路径(例如 ~/.virtualenvs/myenv/lib/python2.6)。但是,如果当前工作目录不是 /usr/lib/python2.6,则该命令会返回正确路径 /usr/lib/python2.6。因此,我昨晚没有吸毒。 - saltycrane
4
这是一个相当不错的解决方案,但它未包含诸如 sys 之类的核心库。 - Adam Spiers
2
这不会打印像datetimeitertoolsmath这样的模块,它们位于lib-dynload/目录中并具有.so扩展名(在我的机器上)。也不会打印importlib,它位于importlib/__init__.py中。 - zahypeti
显示剩余3条评论

13

10
在Python 3.10中,现在有一个名为“sys.stdlib_module_names”的模块,详细信息请参见此处

值得注意的是,这仅适用于Python解释器使用:https://dev59.com/KGsy5IYBdhLWcg3wtwQd#42673644 - MinneapolisCoder9
1
@MinneapolisCoder9 你把 sys.stdlib_module_namessys.builtin_module_names 搞混了。 - wim
你完全正确,@wim。感谢您发现并纠正我的错误。 - MinneapolisCoder9

5

这里对Caspar的回答进行了改进,它不是跨平台的,也忽略了顶级模块(例如email),动态加载的模块(例如array)和核心内置模块(例如sys):

import distutils.sysconfig as sysconfig
import os
import sys

std_lib = sysconfig.get_python_lib(standard_lib=True)

for top, dirs, files in os.walk(std_lib):
    for nm in files:
        prefix = top[len(std_lib)+1:]
        if prefix[:13] == 'site-packages':
            continue
        if nm == '__init__.py':
            print top[len(std_lib)+1:].replace(os.path.sep,'.')
        elif nm[-3:] == '.py':
            print os.path.join(prefix, nm)[:-3].replace(os.path.sep,'.')
        elif nm[-3:] == '.so' and top[-11:] == 'lib-dynload':
            print nm[0:-3]

for builtin in sys.builtin_module_names:
    print builtin

这仍然不完美,因为它会忽略像os.path这样的东西,它是通过类似于import posixpath as path的代码在平台相关的方式中从os.py内定义的,但这可能是你能得到的最好的结果,要记住Python是一种动态语言,直到运行时才能真正知道哪些模块被定义。


1
这会让你接近目标:
import sys; import glob
glob.glob(sys.prefix + "/lib/python%d.%d" % (sys.version_info[0:2]) + "/*.py")

关于ignore-dir选项的另一种可能性:

os.pathsep.join(sys.path)

1
我刚刚意识到,当我在虚拟环境中运行时,sys.prefix返回的路径不包括大多数标准库模块。我已经编辑了上面的问题。 - saltycrane

1

在 @Edmund 的回答基础上,这个解决方案从官方网站获取列表:

def standard_libs(version=None, top_level_only=True):
    import re
    from urllib.request import urlopen
    if version is None:
        import sys
        version = sys.version_info
        version = f"{version.major}.{version.minor}"
    url = f"https://docs.python.org/{version}/py-modindex.html"
    with urlopen(url) as f:
        page = f.read()
    modules = set()
    for module in re.findall(r'#module-(.*?)[\'"]',
                             page.decode('ascii', 'replace')):
        if top_level_only:
            module = module.split(".")[0]
        modules.add(module)
    return modules

它返回一个集合。例如,这是在3.5和3.10之间添加的模块:

>>> standard_libs("3.10") - standard_libs("3.5")
{'contextvars', 'dataclasses', 'graphlib', 'secrets', 'zoneinfo'}

由于这是基于官方文档的,因此不包括未记录的模块,例如:

  • 彩蛋,即thisantigravity
  • 内部模块,例如genericpathposixpathntpath,不应直接使用(应改用os.path)。其他内部模块:idlelib(实现IDLE编辑器)、opcodesre_constantssre_compilesre_parsepyexpatpydoc_datant
  • 所有名称以下划线开头的模块(也是内部模块),除了__main__'、'_thread'和'__future__之外,它们是公共的并且有记录。

如果您担心网站可能会关闭,您可以将列表缓存到本地。例如,您可以使用以下函数创建一个包含所有模块名称的小型Python模块:

def create_stdlib_module_names(
        module_name="stdlib_module_names",
        variable="stdlibs",
        version=None,
        top_level_only=True):
    stdlibs = standard_libs(
        version=version, top_level_only=top_level_only)
        with open(f"{module_name}.py", "w") as f:
            f.write(f"{variable} = {stdlibs!r}\n")

以下是如何使用它的方法:

>>> create_stdlib_module_names()  # run this just once
>>> from stdlib_module_names import stdlibs
>>> len(stdlibs)
207
>>> "collections" in stdlibs
True
>>> "numpy" in stdlibs
False

非常好。谢谢。 - AlexElizard

1

如果您无法运行3.10版本,以下翻译可能不是完美的,但应该可以让您接近所需:

import os
import distutils.sysconfig

def get_stdlib_module_names():
    stdlib_dir = distutils.sysconfig.get_python_lib(standard_lib=True)
    return {f.replace(".py", "") for f in os.listdir(stdlib_dir)}

这里缺少一些模块,例如sys, math, time, 和 itertools

我的使用场景是记录应用程序运行期间导入的模块,因此对于stdlib模块进行粗略过滤就可以。我将其作为集合而不是列表返回,以便于进行成员检查。


  • {"site-packages"} 然后我认为你就差不多了
- xyzzyqed

0
这适用于Python版本大于等于3.4,并且可以轻松导入。
import distutils.sysconfig as sysconfig

python_version = float(sysconfig.get_python_version())

# pathlib was introduced in python 3.4
try:
    from pathlib import Path
except ImportError as err:
    raise ImportError("{}. Python >= 3.4 is required to use get_stdlib but you have {}".format(err, python_version))

def list_stdlib() -> frozenset:
    """
    Get a list of the Python standard library for the current Python version.
    
    Returns:
        list: List of standard library module names.
    """
    std_lib_path = Path(sysconfig.get_python_lib(standard_lib=True))
    std_lib_glob = std_lib_path.glob('*')
    std_lib = set()

    for mod in std_lib_glob:
        if mod.stem.startswith('_') or mod.stem == 'LICENSE': 
            continue
        if mod.suffix == '.py' and mod.parent == std_lib_path:
            std_lib.add(mod.stem)
        elif mod.is_dir() and mod.parent == std_lib_path:
            std_lib.add(mod.stem)

    return frozenset(std_lib)

def main():
    if python_version >= 3.10:
        return sysconfig.sys.stdlib_module_names
    return list_stdlib()
    
if __name__ == "__main__":
    print(list_stdlib())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接