【问题标题】:How to check if python package is latest version programmatically?如何以编程方式检查python包是否是最新版本?
【发布时间】:2020-02-27 03:09:37
【问题描述】:

如何在脚本中以编程方式检查包是否为最新版本并返回真或假?

我可以用这样的脚本检查:

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)

但是如何以编程方式检查并返回真假?

【问题讨论】:

标签: python pip gekko


【解决方案1】:

快速版本(仅检查包)

下面的代码调用具有不可用版本的包,例如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

Edit 2021:下面的代码不再适用于新版本的 pip

以下代码调用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

【讨论】:

  • 我已经更新了。现在它只检查用户感兴趣的包。我已经放了两种选择。
  • 通常if (boolean): return True else: return Falsereturn boolean 更好
  • 使用“0”作为伪造的版本号是不好的,因为有时这只会继续安装包! (例如尝试pip install p==0)。
  • 我用过random 肯定没有随机版本
  • 最新版本的技巧在当前版本的 pip 中不再有效。
【解决方案2】:

我的项目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 转义码,导致结果不可读。我想你可能想解决这个问题?
  • 我有 colorama==0.4.1 和 structlog==19.2.0,是的,我也看到带有该命令的转义码。如果有帮助,我将在 Windows 10.0.17763.195、Python 3.7.4 上运行它。很遗憾,我现在没有机会发布问题。
  • @user541686 这是issue232,已在今天发布的 structlog v20.1.0 中解决。感谢您的报告。
【解决方案3】:

检查安装版本:

检查已安装版本的一种方法是访问顶级命名空间的__version__ 属性:

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

不幸的是,并非所有项目都设置此属性,这只是 Python 中的常见约定。当它们没有版本属性时,您可以使用importlib.metadata 来查询包版本。这种方式实际上不需要导入包本身,因为它是从包元数据中检索的,该元数据在安装包时被写出。

>>> import importlib.metadata
>>> importlib.metadata.version("gekko")
'0.2.0'

此功能从 Python 3.8 开始可用。在较旧的 Python 版本中,您可以类似地使用 pkg_resources,它是 setuptools 的一部分:

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

检查最新版本:

目前没有办法在 stdlib 中执行此操作。但是我的项目luddite有这个功能:

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

【讨论】:

    【解决方案4】:

    仅检查 PyPi 上的最新版本(适用于 Python 3.6 及更高版本):

    $ pip install requests

    import requests
    
    package = 'django'  # replace with the package you want to check
    response = requests.get(f'https://pypi.org/pypi/{package}/json')
    latest_version = response.json()['info']['version']
    

    【讨论】:

      【解决方案5】:

      至少出于演示目的,这应该可以解决问题。只需致电isLatestVersion 并提供您要检查的包的名称。如果您在重要的地方使用它,您可能想尝试/捕获 url 请求,因为互联网访问可能不可用。另请注意,如果未安装该软件包,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
      

      【讨论】:

      • pip._internal 不是公共 API。 pip's docs 中甚至明确不鼓励这样做:“你不能以这种方式使用 pip 的内部 API”。
      • @wim 很高兴知道。我没有意识到这一点。谢谢你让我知道。我肯定会推荐人们使用 Yusuf Baktir 的方法,因为它更简单。
      【解决方案6】:

      编辑:删除点搜索

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

      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 更快。这不是最可靠的方法,因为如果当前安装的版本是0.2.3 但最新版本是0.2.300.2.3a,它将返回不正确的True。一种改进是以编程方式获取最新版本并进行直接比较。

      【讨论】:

      • 小心pip search。它使用了已弃用的 XML-RPC API,有时搜索 results are inaccurate/wrong 实际上我认为它可能很快就会被删除,请参阅 Remove the pip search command #5216
      • 我修改了代码,使用了大牛拉取当前包版本的方法。获取当前 gekko 版本的另一种方法是执行 import gekko 然后 current_version=gekko.__version__ 而不是创建所有包版本的字典。但是,并非所有包都具有可在包中访问的版本号。
      【解决方案7】:

      querying the 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 环境变量),这会给 pip 提供不同的结果
      【解决方案8】:

      根据您可以接受的答案:

      pip list --outdated | grep name_of_package
      

      【讨论】:

        猜你喜欢
        • 2012-01-04
        • 2017-11-25
        • 1970-01-01
        • 1970-01-01
        • 2021-05-03
        • 1970-01-01
        • 2012-08-28
        • 2013-05-25
        • 2011-12-10
        相关资源
        最近更新 更多