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]
#!/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] <archive url/path> ..."""
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())
示例输出
./list-pkg-versions.py list-pkg-versions pika django
pip install hell==99999
ERROR: Could not find a version that satisfies the requirement hell==99999
(from versions: 0.1.0, 0.2.0, 0.2.1, 0.2.2, 0.2.3, 0.2.4, 0.3.0,
0.3.1, 0.3.2, 0.3.3, 0.3.4, 0.4.0, 0.4.1)
ERROR: No matching distribution found for hell==99999
我通常运行pip install packagename==somerandomstring。这返回错误说Could not find a version that satisfies the requirement packagename==somerandomstring,除了该错误,pip还将列出服务器上的可用版本。
e. g.
$ pip install flask==aksjflashd
Collecting flask==aksjflashd
Could not find a version that satisfies the requirement flask==aksjflashd
(from versions: 0.1, 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, 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.12.5, 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.1.0, 1.1.1, 1.1.2)
No matching distribution found for flask==aksjflashd
$
import re
import subprocess
from packaging.version import VERSION_PATTERN as _VRESION_PATTERN
VERSION_PATTERN = re.compile(_VRESION_PATTERN , re.VERBOSE | re.IGNORECASE)
def get_available_versions(package_name):
process = subprocess.run(['pip', 'install', f'{package_name}=='], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
versions = []
for line in process.stderr.decode('utf-8').splitlines():
if 'Could not find a version that satisfies the requirement' in line:
for match in VERSION_PATTERN.finditer(line.split('from versions:')[1]):
versions.append(match.group(0))
return versions