如何检查模块/库/包是不是是 python 标准库的一部分?

Posted

技术标签:

【中文标题】如何检查模块/库/包是不是是 python 标准库的一部分?【英文标题】:How to check if a module/library/package is part of the python standard library?如何检查模块/库/包是否是 python 标准库的一部分? 【发布时间】:2014-04-07 08:54:16 【问题描述】:

我已经用 pip 安装了很多库/模块/包,现在我无法区分哪些是 Python 标准库的原生库,哪些不是。当我的代码在我的机器上运行但在其他任何地方都不起作用时,这会导致问题。

如何检查我在代码中导入的模块/库/包是否来自 python stdlib?

假设检查是在具有所有外部库/模块/包的机器上完成的,否则我可以简单地在没有它们的另一台机器上执行 try-except 导入。

例如,我确信这些导入可以在我的机器上运行,但是当它在仅安装了普通 Python 的机器上时,它会中断:

from bs4 import BeautifulSoup
import nltk
import PIL
import gensim

【问题讨论】:

我猜你可以从这里开始:docs.python.org/2/library/index.html 使用 virtualenv。它是专门为避免这些问题而构建的。 这不是一个答案,但如果您关注PEP-0008,则更容易看出哪些imports 是第三方 'native python' 通常指的是'pure python',例如不使用任何 C 或其他非 Python 语言扩展。相反,您是在谈论标准库与外部依赖项。 啊,@martijnpieters,感谢关于“本机 python”的说明。 =) 【参考方案1】:

@Bakuriu 的回答对我非常有用。我遇到的唯一问题是,如果您想检查特定模块是否是 stdlib,但是否已经导入。在这种情况下,sys.modules 将只有一个条目,因此即使 sys.path 被剥离,导入也会成功:

In [1]: import sys

In [2]: import virtualenv

In [3]: sys.path = []

In [4]: try:
    __import__('virtualenv')
except ImportError:
    print(False)
else:
    print(True)
   ...:
True

In [1]: import sys

In [2]: sys.path = []

In [3]: try:
    __import__('virtualenv')
except ImportError:
    print(False)
else:
    print(True)
   ...:
False

我提出了以下似乎适用于 Python2 和 Python3 的解决方案:

from __future__ import unicode_literals, print_function
import sys
from contextlib import contextmanager
from importlib import import_module


@contextmanager
def ignore_site_packages_paths():
    paths = sys.path
    # remove all third-party paths
    # so that only stdlib imports will succeed
    sys.path = list(filter(
        None,
        filter(lambda i: 'site-packages' not in i, sys.path)
    ))
    yield
    sys.path = paths


def is_std_lib(module):
    if module in sys.builtin_module_names:
        return True

    with ignore_site_packages_paths():
        imported_module = sys.modules.pop(module, None)
        try:
            import_module(module)
        except ImportError:
            return False
        else:
            return True
        finally:
            if imported_module:
                sys.modules[module] = imported_module

可以关注源代码here

【讨论】:

【参考方案2】:

您必须检查所有已导入的模块,以查看其中是否有任何模块位于标准库之外。

以下脚本不是防弹的,但应该为您提供一个起点:

import sys
import os

external = set()
exempt = set()
paths = (os.path.abspath(p) for p in sys.path)
stdlib = p for p in paths
          if p.startswith((sys.prefix, sys.real_prefix)) 
          and 'site-packages' not in p
for name, module in sorted(sys.modules.items()):
    if not module or name in sys.builtin_module_names or not hasattr(module, '__file__'):
        # an import sentinel, built-in module or not a real module, really
        exempt.add(name)
        continue

    fname = module.__file__
    if fname.endswith(('__init__.py', '__init__.pyc', '__init__.pyo')):
        fname = os.path.dirname(fname)

    if os.path.dirname(fname) in stdlib:
        # stdlib path, skip
        exempt.add(name)
        continue

    parts = name.split('.')
    for i, part in enumerate(parts):
        partial = '.'.join(parts[:i] + [part])
        if partial in external or partial in exempt:
            # already listed or exempted
            break
        if partial in sys.modules and sys.modules[partial]:
            # just list the parent name and be done with it
            external.add(partial)
            break

for name in external:
    print name, sys.modules[name].__file__

这是一个新模块,脚本中的所有导入之后导入它,它会打印它认为不是标准库一部分的所有模块.

【讨论】:

【参考方案3】:

标准库在python的documentation中定义。您可以在那里搜索,或将模块名称放入列表中并以编程方式检查。

另外,在 python3.4 中有一个新的isolated mode 允许忽略一定数量的用户定义的库路径。在以前的python 版本中,您可以使用-s 忽略每个用户的环境,使用-E 忽略系统定义的变量。

在python2中,检查模块是否属于标准库的一种非常简单的方法是清除sys.path

>>> import sys
>>> sys.path = []
>>> import numpy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named numpy
>>> import traceback
>>> import os
>>> import re

但是这在 python3.3+ 中不起作用:

>>> import sys
>>> sys.path = []
>>> import traceback
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'traceback'
[...]

这是因为从 python3.3 开始,导入机制发生了变化,导入标准库使用与导入任何其他模块相同的机制(请参阅documentation)。

在 python3.3 中,确保只有 stdlib 的导入成功的唯一方法是仅将标准库路径添加到 sys.path,例如:

>>> import os, sys, traceback
>>> lib_path = os.path.dirname(traceback.__file__)
>>> sys.path = [lib_path]
>>> import traceback
>>> import re
>>> import numpy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'numpy'

我使用traceback 模块来获取库路径,因为这应该适用于任何系统。

对于作为标准库模块的子集的内置模块,您可以查看sys.builtin_module_names

【讨论】:

但这不是关于 stdlib 与第三方的对比,而是非纯第三方与纯 Python 第三方的对比。 @tripleee:我不同意;我认为OP在这里混合了术语。 bs4 是纯 Python,而 PIL 肯定不是。两者都不会安装在新机器上。 @triplee,这是关于 stdlib 与第三方的。因为第三方库没有安装在我的合作者的机器上,所以他们把他们惹恼了……给我留下了诸如“你怎么能在两行中做 X?”之类的问题。 ...大声笑

以上是关于如何检查模块/库/包是不是是 python 标准库的一部分?的主要内容,如果未能解决你的问题,请参考以下文章

python标准库介绍——13 types 模块详解

【python】库、包、模块之间的区别和联系

python常用标准库(压缩包模块zipfile和tarfile)

Python强大的自有模块——标准库

Python强大的自有模块——标准库

1Python标准库系列之模块介绍