Python and pip, list all versions of a package that's available?
考虑到可以与pip和virtualenv一起安装的python(2.x)包的名称,有没有办法找到pip可以安装的所有可能版本的列表?现在是试错法。
我正在尝试为第三方库安装一个版本,但最新版本太新,进行了向后不兼容的更改。所以我想以某种方式列出Pip知道的所有版本,这样我就可以测试它们了。
对于pip>=9.0,使用
1 2 3 4 5 6 7 | $ pip install pylibmc== Collecting pylibmc== Could not find a version that satisfies the requirement pylibmc== (from versions: 0.2, 0.3, 0.4, 0.5.1, 0.5.2, 0.5.3, 0.5.4, 0.5.5, 0.5, 0.6.1, 0.6, 0.7.1, 0.7.2, 0.7.3, 0.7.4, 0.7, 0.8.1, 0.8.2, 0.8, 0.9.1, 0.9.2, 0.9, 1.0-alpha, 1.0-beta, 1.0, 1.1.1, 1.1, 1.2.0, 1.2.1, 1.2.2, 1.2.3, 1.3.0) No matching distribution found for pylibmc== |
–所有可用版本都将打印出来,而无需实际下载或安装任何附加软件包。
对于PIP<9.0,使用
1 | pip install pylibmc==blork |
其中
Pastebin的脚本确实有效。但是,如果您使用多个环境/主机,这就不太方便了,因为您每次都必须复制/创建它。
更好的全面解决方案是使用yolk,它可以与pip一起安装。例如,要查看哪些版本的Django可用:
1 2 3 4 5 6 7 8 9 10 11 12 13 | $ pip install yolk3k $ yolk -V django Django 1.3 Django 1.2.5 Django 1.2.4 Django 1.2.3 Django 1.2.2 Django 1.2.1 Django 1.2 Django 1.1.4 Django 1.1.3 Django 1.1.2 Django 1.0.4 |
一个小小的警告:蛋黄取决于分布。这不是一件坏事,但如果出于某种原因需要继续使用(不推荐使用的)python安装工具,这可能是一个问题。
注:我不参与蛋黄的发育。如果某件事看起来不象它应该做的那样工作,那么在这里留下评论就不会有太大的区别。如果可能的话,可以使用Yolk问题跟踪程序,并考虑提交修复程序。
更新:截至2017年9月,该方法不再有效:在PIP 7中删除了
使用
1 2 3 4 5 6 7 | root@node7:~# pip install web.py -v Downloading/unpacking web.py Using version 0.37 (newest of versions: 0.37, 0.36, 0.35, 0.34, 0.33, 0.33, 0.32, 0.31, 0.22, 0.2) Downloading web.py-0.37.tar.gz (90Kb): 90Kb downloaded Running setup.py egg_info for package web.py running egg_info creating pip-egg-info/web.py.egg-info |
要不安装任何软件包,请使用以下解决方案之一:
1 2 3 4 | root@node7:~# pip install --no-deps --no-install flask -v Downloading/unpacking flask Using version 0.10.1 (newest of versions: 0.10.1, 0.10, 0.9, 0.8.1, 0.8, 0.7.2, 0.7.1, 0.7, 0.6.1, 0.6, 0.5.2, 0.5.1, 0.5, 0.4, 0.3.1, 0.3, 0.2, 0.1) Downloading Flask-0.10.1.tar.gz (544Kb): 544Kb downloaded |
或
1 2 3 4 5 | root@node7:~# cd $(mktemp -d) root@node7:/tmp/tmp.c6H99cWD0g# pip install flask -d . -v Downloading/unpacking flask Using version 0.10.1 (newest of versions: 0.10.1, 0.10, 0.9, 0.8.1, 0.8, 0.7.2, 0.7.1, 0.7, 0.6.1, 0.6, 0.5.2, 0.5.1, 0.5, 0.4, 0.3.1, 0.3, 0.2, 0.1) Downloading Flask-0.10.1.tar.gz (544Kb): 4.1Kb downloaded |
用PIP 1.0测试
1 2 | root@node7:~# pip --version pip 1.0 from /usr/lib/python2.7/dist-packages (python 2.7) |
您不需要第三方软件包来获取此信息。pypi为下面的所有包提供简单的JSON源
1 | https://pypi.python.org/pypi/{PKG_NAME}/json |
下面是一些只使用标准库的python代码,它获取所有版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import json import urllib2 from distutils.version import StrictVersion def versions(package_name): url ="https://pypi.python.org/pypi/%s/json" % (package_name,) data = json.load(urllib2.urlopen(urllib2.Request(url))) versions = data["releases"].keys() versions.sort(key=StrictVersion) return versions print" ".join(versions("scikit-image")) |
该代码打印(截至2015年2月23日):
1 2 3 4 5 6 7 8 9 10 | 0.7.2 0.8.0 0.8.1 0.8.2 0.9.0 0.9.1 0.9.2 0.9.3 0.10.0 0.10.1 |
你可以用蛋黄3K来代替蛋黄。蛋黄3K是从原来的蛋黄中提取的一个叉子,它既能支撑Python2也能支撑Python3。
网址:https://github.com/myint/yolk
1 | pip install yolk3k |
我想出了一个非常简单的bash脚本。感谢JQ的作者。
1 2 3 4 5 6 | #!/bin/bash set -e PACKAGE_JSON_URL="https://pypi.python.org/pypi/${1}/json" curl -s"$PACKAGE_JSON_URL" | jq -r '.releases | keys | .[]' | sort -V |
Update: Add sorting by version number.
在看了一会儿PIP的代码之后,看起来负责定位包的代码可以在
下面的代码几乎是原始函数的1:1副本,第114行中的返回更改为返回所有版本。
脚本需要一个包名称作为第一个也是唯一的参数,并返回所有版本。
网址:http://pastebin.com/axzduqhz
我不能保证正确性,因为我不熟悉PIP的代码。但希望这能有所帮助。
样本输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $ python test.py pip Versions of pip 0.8.2 0.8.1 0.8 0.7.2 0.7.1 0.7 0.6.3 0.6.2 0.6.1 0.6 0.5.1 0.5 0.4 0.3.1 0.3 0.2.1 0.2 dev |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | import posixpath import pkg_resources import sys from pip.download import url_to_path from pip.exceptions import DistributionNotFound from pip.index import PackageFinder, Link from pip.log import logger from pip.req import InstallRequirement from pip.util import Inf class MyPackageFinder(PackageFinder): def find_requirement(self, req, upgrade): url_name = req.url_name # Only check main index if index URL is given: main_index_url = None if self.index_urls: # Check that we have the url_name correctly spelled: main_index_url = Link(posixpath.join(self.index_urls[0], url_name)) # This will also cache the page, so it's okay that we get it again later: page = self._get_page(main_index_url, req) if page is None: url_name = self._find_url_name(Link(self.index_urls[0]), url_name, req) or req.url_name # Combine index URLs with mirror URLs here to allow # adding more index URLs from requirements files all_index_urls = self.index_urls + self.mirror_urls def mkurl_pypi_url(url): loc = posixpath.join(url, url_name) # For maximum compatibility with easy_install, ensure the path # ends in a trailing slash. Although this isn't in the spec # (and PyPI can handle it without the slash) some other index # implementations might break if they relied on easy_install's behavior. if not loc.endswith('/'): loc = loc + '/' return loc if url_name is not None: locations = [ mkurl_pypi_url(url) for url in all_index_urls] + self.find_links else: locations = list(self.find_links) locations.extend(self.dependency_links) for version in req.absolute_versions: if url_name is not None and main_index_url is not None: locations = [ posixpath.join(main_index_url.url, version)] + locations file_locations, url_locations = self._sort_locations(locations) locations = [Link(url) for url in url_locations] logger.debug('URLs to search for versions for %s:' % req) for location in locations: logger.debug('* %s' % location) found_versions = [] found_versions.extend( self._package_versions( [Link(url, '-f') for url in self.find_links], req.name.lower())) page_versions = [] for page in self._get_pages(locations, req): logger.debug('Analyzing links from page %s' % page.url) logger.indent += 2 try: page_versions.extend(self._package_versions(page.links, req.name.lower())) finally: logger.indent -= 2 dependency_versions = list(self._package_versions( [Link(url) for url in self.dependency_links], req.name.lower())) if dependency_versions: logger.info('dependency_links found: %s' % ', '.join([link.url for parsed, link, version in dependency_versions])) file_versions = list(self._package_versions( [Link(url) for url in file_locations], req.name.lower())) if not found_versions and not page_versions and not dependency_versions and not file_versions: logger.fatal('Could not find any downloads that satisfy the requirement %s' % req) raise DistributionNotFound('No distributions at all found for %s' % req) if req.satisfied_by is not None: found_versions.append((req.satisfied_by.parsed_version, Inf, req.satisfied_by.version)) if file_versions: file_versions.sort(reverse=True) logger.info('Local files found: %s' % ', '.join([url_to_path(link.url) for parsed, link, version in file_versions])) found_versions = file_versions + found_versions all_versions = found_versions + page_versions + dependency_versions applicable_versions = [] for (parsed_version, link, version) in all_versions: if version not in req.req: logger.info("Ignoring link %s, version %s doesn't match %s" % (link, version, ','.join([''.join(s) for s in req.req.specs]))) continue applicable_versions.append((link, version)) applicable_versions = sorted(applicable_versions, key=lambda v: pkg_resources.parse_version(v[1]), reverse=True) existing_applicable = bool([link for link, version in applicable_versions if link is Inf]) if not upgrade and existing_applicable: if applicable_versions[0][1] is Inf: logger.info('Existing installed version (%s) is most up-to-date and satisfies requirement' % req.satisfied_by.version) else: logger.info('Existing installed version (%s) satisfies requirement (most up-to-date version is %s)' % (req.satisfied_by.version, applicable_versions[0][1])) return None if not applicable_versions: logger.fatal('Could not find a version that satisfies the requirement %s (from versions: %s)' % (req, ', '.join([version for parsed_version, link, version in found_versions]))) raise DistributionNotFound('No distributions matching the version for %s' % req) if applicable_versions[0][0] is Inf: # We have an existing version, and its the best version logger.info('Installed version (%s) is most up-to-date (past versions: %s)' % (req.satisfied_by.version, ', '.join([version for link, version in applicable_versions[1:]]) or 'none')) return None if len(applicable_versions) > 1: logger.info('Using version %s (newest of versions: %s)' % (applicable_versions[0][1], ', '.join([version for link, version in applicable_versions]))) return applicable_versions if __name__ == '__main__': req = InstallRequirement.from_line(sys.argv[1], None) finder = MyPackageFinder([], ['http://pypi.python.org/simple/']) versions = finder.find_requirement(req, False) print 'Versions of %s' % sys.argv[1] for v in versions: print v[1] |
https://pypi.python.org/pypi/django/-适用于维护人员选择显示所有包的包https://pypi.python.org/simple/pip/-无论如何都应该这样做(列出所有链接)
我知道这有点傻,但你可以尝试这样的方法:
1 | pip install django==x |
这将出错,但将列出此包可用的所有版本。
您可以使用这个小的python3脚本使用jsonapi从pypi中获取包的可用版本列表,并按相反的时间顺序打印它们。与这里发布的其他一些python解决方案不同,这并不是针对像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/usr/bin/env python import sys import requests from pkg_resources import parse_version def versions(pkg_name): url = f'https://pypi.python.org/pypi/{pkg_name}/json' releases = requests.get(url).json()['releases'] return sorted(releases, key=parse_version, reverse=True) if __name__ == '__main__': print(*versions(sys.argv[1]), sep=' ') |
保存脚本并以包名称作为参数运行它,例如:
1 2 3 4 5 6 7 8 | python versions.py django 2.2 2.2rc1 2.2b1 2.2a1 2.1.8 2.1.7 ... |
PIP7.1.0已删除--安装中没有安装选项。我找到了一种方法来获取一个没有任何额外包的包的所有版本。
1 2 3 4 | $ pip install --no-deps Django==x.x.x Collecting Django==x.x.x. Could not find a version that satisfies the requirement Django==x.x.x. (from versions: 1.1.3, 1.1.4, 1.2, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.2.6, 1.2.7, 1.3, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.3.5, 1.3.6, 1.3.7, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.4.8, 1.4.9, 1.4.10, 1.4.11, 1.4.12, 1.4.13, 1.4.14, 1.4.15, 1.4.16, 1.4.17, 1.4.18, 1.4.19, 1.4.20, 1.5, 1.5.1, 1.5.2, 1.5.3, 1.5.4, 1.5.5, 1.5.6, 1.5.7, 1.5.8, 1.5.9, 1.5.10, 1.5.11, 1.5.12, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.6.11, 1.7, 1.7.1, 1.7.2, 1.7.3, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.8a1, 1.8b1, 1.8b2, 1.8rc1, 1.8, 1.8.1, 1.8.2) No matching distribution found for Django==x.x.x. |
这对我在OSX上有效:
'
它每行返回一个列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 1.1.0rc1 1.1.0rc2 1.1.0 1.2.0rc1 1.2.0rc2 1.2.0rc3 1.2.0rc4 1.2.0 1.3.0rc1 1.3.0rc2 1.3.0rc3 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0rc1 1.4.0rc2 1.4.0rc3 1.4.0 1.4.1 1.4.2 1.5.0rc1 1.5.0rc2 1.5.0rc3 1.5.0 1.5.1 1.5.2 1.6.0rc1 1.6.0 1.6.1 1.6.2 1.7.0rc1 1.7.0rc2 1.7.0 1.7.1 1.8.0rc1 1.8.0rc2 1.8.0 1.8.1 1.9.0rc1 1.9.0rc2 1.9.0rc3 1.9.0rc4 1.9.0 1.10.0rc1 1.10.0rc2 1.10.0 |
或获取最新版本:
' | gsort -r -V | head -1
1 | 1.10.0rc2 |
请记住,必须(在OSX上)安装
我对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import json import requests from distutils.version import StrictVersion def versions(package_name): url ="https://pypi.python.org/pypi/{}/json".format(package_name) data = requests.get(url).json() return sorted(list(data["releases"].keys()), key=StrictVersion, reverse=True) >>> print(" ".join(versions("gunicorn"))) 19.1.1 19.1.0 19.0.0 18.0 17.5 0.17.4 0.17.3 ... |
另一种解决方案是使用仓库API:
https://warehouse.readthedocs.io/api参考/json/发布
例如,对于烧瓶:
1 2 3 | import requests r = requests.get("https://pypi.org/pypi/Flask/json") print(r.json()['releases'].keys()) |
将打印:
1 | dict_keys(['0.1', '0.10', '0.10.1', '0.11', '0.11.1', '0.12', '0.12.1', '0.12.2', '0.12.3', '0.12.4', '0.2', '0.3', '0.3.1', '0.4', '0.5', '0.5.1', '0.5.2', '0.6', '0.6.1', '0.7', '0.7.1', '0.7.2', '0.8', '0.8.1', '0.9', '1.0', '1.0.1', '1.0.2']) |
我刚跑过:
1 | pip show packagename |
例如。:
1 2 3 4 5 6 7 8 9 10 11 12 | > pip3 show setuptools --- Metadata-Version: 2.0 Name: setuptools Version: 18.4 Summary: Easily download, build, install, upgrade, and uninstall Python packages Home-page: https://bitbucket.org/pypa/setuptools Author: Python Packaging Authority Author-email: distutils-sig@python.org License: PSF or ZPL Location: /usr/local/lib/python3.4/site-packages Requires: |
下面是当前基于python pip的方法,搜索旧的pypi包api:
1 2 3 4 5 6 7 8 9 | from pip import index import requests finder = index.PackageFinder( [], ['https://pypi.python.org/simple'], session=requests.Session() ) results = finder.find_all_candidates("package_name") versions = [p.version for p in results] |
我的看法是两个已发布的答案的组合,并进行了一些修改,以便在运行的Python环境中更容易使用它们。
其思想是提供一个全新的命令(根据install命令建模),该命令为您提供一个要使用的包查找器实例。其好处在于,它可以处理和使用PIP支持和读取本地PIP配置文件的任何索引,因此您可以获得与常规PIP安装相同的正确结果。
我试图使它与PIP V9.x和10.x兼容。但只在9.x上试过
https://gist.github.com/kaos/68511b013fcdebe766c981f50b473d4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #!/usr/bin/env python # When you want a easy way to get at all (or the latest) version of a certain python package from a PyPi index. import sys import logging try: from pip._internal import cmdoptions, main from pip._internal.commands import commands_dict from pip._internal.basecommand import RequirementCommand except ImportError: from pip import cmdoptions, main from pip.commands import commands_dict from pip.basecommand import RequirementCommand from pip._vendor.packaging.version import parse as parse_version logger = logging.getLogger('pip') class ListPkgVersionsCommand(RequirementCommand): """ List all available versions for a given package from: - PyPI (and other indexes) using requirement specifiers. - VCS project urls. - Local project directories. - Local or remote source archives. """ name ="list-pkg-versions" usage =""" %prog [options] <requirement specifier> [package-index-options] ... %prog [options] [-e] <vcs project url> ... %prog [options] [-e] <local project path> ... %prog [options] ...""" summary = 'List package versions.' def __init__(self, *args, **kw): super(ListPkgVersionsCommand, self).__init__(*args, **kw) cmd_opts = self.cmd_opts cmd_opts.add_option(cmdoptions.install_options()) cmd_opts.add_option(cmdoptions.global_options()) cmd_opts.add_option(cmdoptions.use_wheel()) cmd_opts.add_option(cmdoptions.no_use_wheel()) cmd_opts.add_option(cmdoptions.no_binary()) cmd_opts.add_option(cmdoptions.only_binary()) cmd_opts.add_option(cmdoptions.pre()) cmd_opts.add_option(cmdoptions.require_hashes()) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, self.parser, ) self.parser.insert_option_group(0, index_opts) self.parser.insert_option_group(0, cmd_opts) def run(self, options, args): cmdoptions.resolve_wheel_no_use_binary(options) cmdoptions.check_install_build_global(options) with self._build_session(options) as session: finder = self._build_package_finder(options, session) # do what you please with the finder object here... ;) for pkg in args: logger.info( '%s: %s', pkg, ', '.join( sorted( set(str(c.version) for c in finder.find_all_candidates(pkg)), key=parse_version, ) ) ) commands_dict[ListPkgVersionsCommand.name] = ListPkgVersionsCommand if __name__ == '__main__': sys.exit(main()) |
实例输出
1 2 3 | $ ./list-pkg-versions.py list-pkg-versions pika django pika: 0.5, 0.5.1, 0.5.2, 0.9.1a0, 0.9.2a0, 0.9.3, 0.9.4, 0.9.5, 0.9.6, 0.9.7, 0.9.8, 0.9.9, 0.9.10, 0.9.11, 0.9.12, 0.9.13, 0.9.14, 0.10.0b1, 0.10.0b2, 0.10.0, 0.11.0b1, 0.11.0, 0.11.1, 0.11.2, 0.12.0b2 django: 1.1.3, 1.1.4, 1.2, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.2.6, 1.2.7, 1.3, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.3.5, 1.3.6, 1.3.7, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.4.8, 1.4.9, 1.4.10, 1.4.11, 1.4.12, 1.4.13, 1.4.14, 1.4.15, 1.4.16, 1.4.17, 1.4.18, 1.4.19, 1.4.20, 1.4.21, 1.4.22, 1.5, 1.5.1, 1.5.2, 1.5.3, 1.5.4, 1.5.5, 1.5.6, 1.5.7, 1.5.8, 1.5.9, 1.5.10, 1.5.11, 1.5.12, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.6.11, 1.7, 1.7.1, 1.7.2, 1.7.3, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.7.9, 1.7.10, 1.7.11, 1.8a1, 1.8b1, 1.8b2, 1.8rc1, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.8.5, 1.8.6, 1.8.7, 1.8.8, 1.8.9, 1.8.10, 1.8.11, 1.8.12, 1.8.13, 1.8.14, 1.8.15, 1.8.16, 1.8.17, 1.8.18, 1.8.19, 1.9a1, 1.9b1, 1.9rc1, 1.9rc2, 1.9, 1.9.1, 1.9.2, 1.9.3, 1.9.4, 1.9.5, 1.9.6, 1.9.7, 1.9.8, 1.9.9, 1.9.10, 1.9.11, 1.9.12, 1.9.13, 1.10a1, 1.10b1, 1.10rc1, 1.10, 1.10.1, 1.10.2, 1.10.3, 1.10.4, 1.10.5, 1.10.6, 1.10.7, 1.10.8, 1.11a1, 1.11b1, 1.11rc1, 1.11, 1.11.1, 1.11.2, 1.11.3, 1.11.4, 1.11.5, 1.11.6, 1.11.7, 1.11.8, 1.11.9, 1.11.10, 1.11.11, 1.11.12, 2.0, 2.0.1, 2.0.2, 2.0.3, 2.0.4 |
你可以提高你的
1 2 3 | -> % pip list | grep 'beautifulsoup4' beautifulsoup4 (4.4.1) |