如何以编程方式检查python软件包是否为最新版本?


29

如何在脚本中以编程方式检查软件包的最新版本,并返回true或false?

我可以使用以下脚本进行检查:

package='gekko'
import pip
if hasattr(pip, 'main'):
    from pip import main as pipmain
else:
    from pip._internal import main as pipmain
pipmain(['search','gekko'])

或使用命令行:

(base) C:\User>pip search gekko
gekko (0.2.3)  - Machine learning and optimization for dynamic systems
  INSTALLED: 0.2.3 (latest)

但是,如何以编程方式检查并返回true或false?


4
不是一个完整的解决方案,但可能会给您一些想法。stackoverflow.com/questions/4888027/…–
reyPanda

点子没有可以调用的api吗?
阿兰·哈达德

3
如果可以使用它,Python 3.8至少在其本地安装的方面改进了对此类内容的支持。 docs.python.org/3/library/importlib.metadata.html
JL Peyret

1
pip没有API。您可能想观看pip-api项目,但是目前还没有太多。
wim

Answers:


16

快速版本(仅检查软件包)

下面的代码使用不可用的版本(如)调用该软件包pip install package_name==random。该调用将返回所有可用版本。该程序将读取最新版本。

然后,程序运行pip show package_name并获取软件包的当前版本。

如果找到匹配项,则返回True,否则返回False。

考虑到它是一个可靠的选择 pip

import subprocess
import sys
def check(name):
    latest_version = str(subprocess.run([sys.executable, '-m', 'pip', 'install', '{}==random'.format(name)], capture_output=True, text=True))
    latest_version = latest_version[latest_version.find('(from versions:')+15:]
    latest_version = latest_version[:latest_version.find(')')]
    latest_version = latest_version.replace(' ','').split(',')[-1]

    current_version = str(subprocess.run([sys.executable, '-m', 'pip', 'show', '{}'.format(name)], capture_output=True, text=True))
    current_version = current_version[current_version.find('Version:')+8:]
    current_version = current_version[:current_version.find('\\n')].replace(' ','') 

    if latest_version == current_version:
        return True
    else:
        return False

以下代码要求pip list --outdated

import subprocess
import sys

def check(name):
    reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'list','--outdated'])
    outdated_packages = [r.decode().split('==')[0] for r in reqs.split()]
    return name in outdated_packages

我已经更新了。现在,它仅检查用户感兴趣的软件包。我已经提出了两种选择。
优素福·巴克蒂

1
通常if (boolean): return True else: return False情况下,最好return boolean
-wim

使用“ 0”作为伪造的版本号是不好的,因为有时这只会继续安装软件包!(pip install p==0例如尝试)。
wim

我已经使用过,random我确定没有随机版本
Yusuf Baktir

10

我的项目johnnydep具有此功能。

在外壳中:

pip install --upgrade pip johnnydep
pip install gekko==0.2.0

在Python中:

>>> from johnnydep.lib import JohnnyDist
>>> dist = JohnnyDist("gekko")
>>> dist.version_installed  
'0.2.0'
>>> dist.version_latest 
'0.2.3'

当我在Windows命令提示符中运行此命令时,我得到了使结果不可读的ANSI转义代码。我认为您可能要解决此问题?
user541686 '19

我有colorama == 0.4.1和structlog == 19.2.0,是的,我也确实看到了该命令的转义代码。如果有帮助,请在Windows 10.0.17763.195,Python 3.7.4上运行。很遗憾,我现在没有机会发布问题。
user541686 '19

@ user541686这是issue232,已在今天发布的structlog v20.1.0中解决。感谢您的报告。
wim

很好,谢谢!
user541686

4

编辑:删除点子搜索

感谢您的几个建议。这是一个不使用的新版本,pip search而是直接从Daniel Hill的pypi建议中获取最新版本。这也解决了子字符串错误匹配的问题。

def check(name):
    import subprocess
    import sys
    import json
    import urllib.request

    # create dictionary of package versions
    pkgs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
    keys = [p.decode().split('==')[0] for p in pkgs.split()]
    values = [p.decode().split('==')[1] for p in pkgs.split()]
    d = dict(zip(keys, values)) # dictionary of all package versions

    # retrieve info on latest version
    contents = urllib.request.urlopen('https://pypi.org/pypi/'+name+'/json').read()
    data = json.loads(contents)
    latest_version = data['info']['version']

    if d[name]==latest_version:
        print('Latest version (' + d[name] + ') of '+str(name)+' is installed')
        return True
    else:
        print('Version ' + d[name] + ' of '+str(name)+' not the latest '+latest_version)
        return False

print(check('gekko'))

原始回应

这是一个快速解决方案,它仅检索gekko感兴趣的软件包上的最新版本信息。

def check(name):
    import subprocess
    import sys
    # create dictionary of package versions
    pkgs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
    keys = [p.decode().split('==')[0] for p in pkgs.split()]
    values = [p.decode().split('==')[1] for p in pkgs.split()]
    d = dict(zip(keys, values)) # dictionary of all package versions

    # retrieve info on latest version
    s = subprocess.check_output([sys.executable, '-m', 'pip', 'search', name])

    if d[name] in s.decode():  # weakness
        print('Latest version (' + d[name] + ') of '+str(name)+' is installed')
        return True
    else:
        print(s.decode())
        return False

print(check('gekko'))

这将产生消息,Latest version (0.2.3) of gekko is installed并返回True以指示最新版本(False如果不是最新版本)。这可能不是最好的解决方案,因为它仅使用来检查版本子字符串,if d[name] in s.decode():但比pip list --outdated检查所有软件包要快。这不是最可靠的方法,因为True如果当前安装的版本为,0.2.3而最新版本为0.2.30或,则它将返回错误0.2.3a。一种改进是以编程方式获取最新版本并进行直接比较。


小心一点pip search。它使用了已弃用的XML-RPC API,有时搜索结果不正确/错误 。事实上,我认为可能很快将其删除,请参阅删除pip搜索命令#5216
wim

我修改了代码,以使用Daniel提取当前软件包版本的方法。获取当前gekko版本的另一种方法是执行操作import gekkocurrent_version=gekko.__version__而不是创建所有软件包版本的字典。但是,并非所有软件包都具有可在该软件包中访问的版本号。
约翰·海登格伦

4

最新版本:

我的项目luddite具有以下功能:

>>> import luddite
>>> luddite.get_version_pypi("gekko")
'0.2.3'

安装版本:

检查已安装版本的规范方法只是访问 __version__顶级名称空间属性:

>>> import gekko
>>> gekko.__version__
'0.2.0'

不幸的是,并非所有项目都设置此属性。如果没有,您可以使用pkg_resources它们从元数据中挖掘出来:

>>> import pkg_resources
>>> pkg_resources.get_distribution("gekko").version
'0.2.0'

2

这样做至少可以达到演示目的。只需isLatestVersion使用您要检查的包裹名称致电即可。如果您在重要的地方使用此功能,则可能会尝试/捕获url请求,因为可能无法访问Internet。另请注意,如果未安装该软件包,isLatestVersion则将返回False。

已针对Python 3.7.4和Pip 19.0.3进行了测试。

import pip
import subprocess
import json
import urllib.request
from pip._internal.operations.freeze import freeze

def isLatestVersion(pkgName):
    # Get the currently installed version
    current_version = ''
    for requirement in freeze(local_only=False):
        pkg = requirement.split('==')
        if pkg[0] == pkgName:
            current_version = pkg[1]

    # Check pypi for the latest version number
    contents = urllib.request.urlopen('https://pypi.org/pypi/'+pkgName+'/json').read()
    data = json.loads(contents)
    latest_version = data['info']['version']

    return latest_version == current_version

1
pip._internal不是公共API。pip的文档中甚至明确建议不要使用它:“ 您不得以这种方式使用pip的内部API ”。
WIM

@wim很高兴知道。我不知道这一点。谢谢你让我知道。我绝对会建议人们使用Yusuf Baktir的方法来代替,因为它更简单。
丹尼尔·希尔

2

通过查询PyPI API自己编写一个简单的脚本并不难。使用最新的Python 3.8,可以仅使用标准库(使用Python 3.7或更旧版本时,您必须安装importlib_metadata反向端口):

# check_version.py

import json
import urllib.request
import sys

try:
    from importlib.metadata import version
except ImportError:
    from importlib_metadata import version

from distutils.version import LooseVersion


if __name__ == '__main__':
    name = sys.argv[1]
    installed_version = LooseVersion(version(name))

    # fetch package metadata from PyPI
    pypi_url = f'https://pypi.org/pypi/{name}/json'
    response = urllib.request.urlopen(pypi_url).read().decode()
    latest_version = max(LooseVersion(s) for s in json.loads(response)['releases'].keys())

    print('package:', name, 'installed:', installed_version, 'latest:', latest_version)

用法示例:

$ python check_version.py setuptools
package: setuptools installed: 41.2.0 latest: 41.6.0

如果您碰巧已经packaging安装了,那么它是一个更好的选择distutils.version进行版本分析:

from distutils.version import LooseVersion

...

LooseVersion(s)

变成

from packaging.version import parse

...

parse(s)

如果为用户配置了其他索引或备用索引(通过pip.conf文件或PIP_INDEX_URL或PIP_EXTRA_INDEX_URL env vars),这可能会给pip提供不同的结果
48
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.