我正在编写一段代码,用于报告和协调两个由pip管理的Python安装之间的差异。
如何在不调用pip子程序的情况下以编程方式获取pip list
提供的信息?
我正在编写一段代码,用于报告和协调两个由pip管理的Python安装之间的差异。
如何在不调用pip子程序的情况下以编程方式获取pip list
提供的信息?
截至2019年2月1日的最佳答案已经过时,无法与更新的pip版本一起使用。
但是不用担心 - 仍然可以通过编程方式获取软件包列表:
A. _internal.main
from pip import _internal
_internal.main(['list'])
这将打印出三列,分别是“Package”、“Version”和“Location”。
请注意,不推荐使用pip的内部api。
B. pkg_resources
import pkg_resources
print([p.project_name for p in pkg_resources.working_set])
# note that this is same as calling pip._vendor.pkg_resources.working_set
C. iter_modules
执行时间较长(在配备I5 CPU,SSD和8GB内存的计算机上约为300毫秒)。
好处是它将具有更广泛的模块列表,并输出可导入的名称。
例如:python-dateutil 作为 dateutil 被导入,但 iter_modules 将给出导入名称:dateutil。
from pkgutil import iter_modules
print([p.name for p in iter_modules()])
D. 通过subprocess在命令行中调用pip
这个问题的解决方案很简单,我会把它留给读者作为练习。
也就是说我懒得做这个,祝你好运!:D
pkg_resources
是最好的方法,因为返回的对象有一个 .version
成员变量来包含它。 - KonstantinPython 3.6和Pip 19.0.1的更新
> from pip._internal.utils.misc import get_installed_distributions
> p = get_installed_distributions()
> pprint.pprint(p)
[wheel 0.32.3 (/usr/local/lib/python3.7/site-packages),
wcwidth 0.1.7 (/usr/local/lib/python3.7/site-packages),
virtualenv 16.0.0 (/usr/local/lib/python3.7/site-packages),
virtualenv-clone 0.3.0 (/usr/local/lib/python3.7/site-packages),
urllib3 1.24.1 (/usr/local/lib/python3.7/site-packages),
typing 3.6.6 (/usr/local/lib/python3.7/site-packages),
terminaltables 3.1.0 (/usr/local/lib/python3.7/site-packages),
...
原始答案
Pip只是Python模块,所以只需导入它并调用list
:
import pip
pip.main(['list'])
# you can get details on package using show:
pip.main(['show', 'wheel'])
pip.utils.get_installed_distributions()
返回已安装软件包列表。
packages = pip.utils.get_installed_distributions()
p = packages[0]
p.project_name
p.version
p.egg_name
p.location
您可以从源代码这里了解pip list
的操作。
get_installed_distributions
还接受许多参数,以仅返回本地软件包(来自当前虚拟环境)等。请参见此处的帮助文档。
还有来自_vendor
模块的底层命令:
list(pip._vendor.pkg_resources.working_set)
然而,get_installed_distributions
提供了更简单的 API。
19.0.1
开始(在Python 3.6上,尽管我认为这并不重要),这个命令可以正常工作:from pip._internal.utils.misc import get_installed_distributions; p = get_installed_distributions()
。 - Joao Coelhopython -m pip list
(robust method)import subprocess
import sys
def pip_list():
args = [sys.executable, "-m", "pip", "list"]
p = subprocess.run(args, check=True, capture_output=True)
return p.stdout.decode()
print(pip_list())
从脚本安装软件包的官方推荐方法是通过调用pip的命令行界面来实现子进程。这里介绍的大多数其他答案都不受pip支持。此外,自pip v10以来,所有代码都已移动到
pip._internal
,以明确向用户表示禁止对pip进行编程使用。使用
sys.executable
确保您将调用与当前运行时相关联的相同pip
。
import os
import subprocess as su
os.system("pip list")
su.call(["pip","list"])
conda list
,但存在相同的整数问题。 - MinneapolisCoder9pip_list = os.popen('pip list').read()
将调用的输出分配给变量。(对于conda list
也是如此)在Unix上,os.system
的返回值是调用的退出状态。 - Ken Jiiii在这里测试了一些解决方案,发现要么非常缓慢,要么已经弃用,要么在我的Python 3.10上返回错误,因此我使用以下解决方案:
注意
此函数可以检索所有已安装的包及其已安装版本。
import pkg_resources
def get_installed_packages():
installed_packages = []
for package in pkg_resources.working_set:
installed_packages.append(package.key)
return installed_packages
def get_package_version(package_name):
try:
return pkg_resources.get_distribution(package_name).version
except pkg_resources.DistributionNotFound:
return None
# Get a list of all installed packages
installed_packages = get_installed_packages()
# Iterate over the installed packages and get their versions
package_versions = {}
for package_name in installed_packages:
version = get_package_version(package_name)
package_versions[package_name] = version
# Print the package versions
for package_name, version in package_versions.items():
print(f"{package_name} - {version}")
示例输出:
xlsxwriter - 3.0.9
argcomplete - 2.0.0
comm - 0.1.2
debugpy - 1.6.6
结合正则表达式列出软件包名称,我们可以检查所有已安装版本是否实际安装在计算机上。
import re
def extract_package_names(file_path):
with open(file_path, 'r') as file:
requirements = file.readlines()
package_names = []
for requirement in requirements:
match = re.search(r'^([\w.-]+)', requirement)
if match:
package_names.append(match.group(1))
return package_names
# Example usage
file_path = 'requirements.txt'
package_names = extract_package_names(file_path)
print(package_names)
示例输出:
['numpy', 'pandas', 'xlsxwriter']
两者结合:
file_path = 'requirements.txt'
package_names = extract_package_names(file_path)
packages = {}
for package_name in package_names:
packages[package_name] = get_package_version(package_name)
packages
pip.main()
想法,通过捕获stdout进行了详细说明。当然,使用get_installed_distributions()
是首选解决方案。import contextlib
@contextlib.contextmanager
def capture():
import sys
from cStringIO import StringIO
oldout,olderr = sys.stdout, sys.stderr
try:
out=[StringIO(), StringIO()]
sys.stdout,sys.stderr = out
yield out
finally:
sys.stdout,sys.stderr = oldout, olderr
out[0] = out[0].getvalue()
out[1] = out[1].getvalue()
with capture() as out:
import pip
pip.main(['list'])
print out
['awscli (1.7.45)\nboto (2.38.0) ...