我想要类似于 sys.builtin_module_names
的东西,但是针对的是标准库。其他没有起作用的方法有:
sys.modules
- 只显示已加载的模块sys.prefix
- 路径会包括非标准库模块,而且在虚拟环境中似乎不起作用。
我希望得到这个列表是为了将其传递给 trace
命令行选项的 --ignore-module
或 --ignore-dir
。
所以最终,我想知道如何在使用 trace
或 sys.settrace
时忽略所有标准库模块。
我通过编写一些代码来刮取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']
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
为什么不自己找出哪些是标准库的一部分呢?
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
可以在虚拟环境中工作。但我希望它既可以在虚拟环境内部,也可以在外部工作。我想我只需要一些逻辑来决定使用哪个前缀就行了。 - saltycranesysconfig.get_python_lib(standard_lib=True)
即使在使用 virtualenv 时也适用于我。 - saltycrane/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
。因此,我昨晚没有吸毒。 - saltycranesys
之类的核心库。 - Adam Spiersdatetime
,itertools
或math
这样的模块,它们位于lib-dynload/
目录中并具有.so
扩展名(在我的机器上)。也不会打印importlib
,它位于importlib/__init__.py
中。 - zahypetisys.stdlib_module_names
和 sys.builtin_module_names
搞混了。 - wim这里对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是一种动态语言,直到运行时才能真正知道哪些模块被定义。
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)
sys.prefix
返回的路径不包括大多数标准库模块。我已经编辑了上面的问题。 - saltycrane在 @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'}
由于这是基于官方文档的,因此不包括未记录的模块,例如:
this
和antigravity
genericpath
、posixpath
或ntpath
,不应直接使用(应改用os.path
)。其他内部模块:idlelib
(实现IDLE编辑器)、opcode
、sre_constants
、sre_compile
、sre_parse
、pyexpat
、pydoc_data
、nt
。__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
如果您无法运行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模块进行粗略过滤就可以。我将其作为集合而不是列表返回,以便于进行成员检查。
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())
sys.stdlib_module_names
仍然缺少像pkg_resources
这样的东西。 - xjclsys.stdlib_module_names
不会包括setuptools
和相关的pkg_resources
。这是因为尽管在venvs中预安装,但从技术上来说它并不属于标准库的一部分。 - xjclstdlib_module_names
没有理由包含setuptools
/pkg_resources
。这些不是标准库,并且从 Python 3.12 开始,也将不再被预装到 venvs 中。 - wim