Python y pip, ¿enumera todas las versiones de un paquete que está disponible?

Dado el nombre de un paquete de Python (2.X) que se puede instalar con pip y virtualenv , ¿hay alguna forma de encontrar una lista de todas las versiones posibles que podría instalar pip? Ahora mismo es prueba y error.

Estoy intentando instalar una versión para una biblioteca de terceros, pero la versión más reciente es demasiado nueva, se hicieron cambios incompatibles hacia atrás. Así que me gustaría tener de alguna manera una lista de todas las versiones que pip sabe, para poder probarlas.

El script en pastebin funciona. Sin embargo, no es muy conveniente si está trabajando con múltiples entornos / hosts porque tendrá que copiarlo / crearlo cada vez.

Una solución más completa sería utilizar yema , que está disponible para instalar con pip. Por ejemplo, para ver qué versiones de Django están disponibles:

 $ 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 

Una advertencia menor: la yema depende de la distribución. Esto no es algo malo, pero puede ser un problema si necesita, por algún motivo, seguir con las herramientas de configuración de Python (en desuso).

Nota: No estoy involucrado en el desarrollo de la yema. Si algo no parece funcionar como debería, dejar un comentario aquí no debería hacer mucha diferencia. Utilice el rastreador de problemas de la yema en su lugar y considere enviar una solución, si es posible.

Para pip> = 9.0 use

 $ 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== 

– todas las versiones disponibles se imprimirán sin descargar ni instalar ningún paquete adicional.

Para uso de pip <9.0

 pip install pylibmc==blork 

donde blork puede ser cualquier cadena que no es probable que sea un candidato de instalación.

Actualizar:
A partir de septiembre de 2017, este método ya no funciona: --no-install en la pip 7

Use pip install -v , puede ver todas las versiones que están disponibles

 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 

Para no instalar ningún paquete, use una de las siguientes soluciones:

 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 

o

 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 

Probado con pip 1.0

 root@node7:~# pip --version pip 1.0 from /usr/lib/python2.7/dist-packages (python 2.7) 

No necesita un paquete de terceros para obtener esta información. pypi proporciona feeds JSON simples para todos los paquetes bajo

 https://pypi.python.org/pypi/{PKG_NAME}/json 

Aquí hay algunos códigos Python que usan solo la biblioteca estándar que obtiene todas las versiones.

 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 "\n".join(versions("scikit-image")) 

Ese código se imprime (a partir del 23 de febrero de 2015):

 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 

Podrías el paquete yolk3k en lugar de yolk. yolk3k es un tenedor de la yema original y es compatible con python2 y 3.

https://github.com/myint/yolk

 pip install yolk3k 

Después de mirar el código de pip por un tiempo, parece que el código responsable de localizar paquetes se puede encontrar en la clase pip.index en pip.index . Su método find_requirement busca las versiones de un InstallRequirement , pero desafortunadamente solo devuelve la versión más reciente.

El código a continuación es casi una copia 1: 1 de la función original, con el retorno en la línea 114 cambiado para devolver todas las versiones.

El script espera un nombre de paquete como primer y único argumento y devuelve todas las versiones.

http://pastebin.com/axzdUQhZ

No puedo garantizar la corrección, ya que no estoy familiarizado con el código de pip. Pero espero que esto ayude.

Salida de muestra

 $ 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 

El código:

 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] 

Se me ocurrió el guión de bash muerto simple. Gracias al autor de jq .

 #!/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 

Actualización: Añadir clasificación por número de versión.

https://pypi.python.org/pypi/Django/ – funciona para paquetes cuyos mantenedores eligen mostrar todos los paquetes https://pypi.python.org/simple/pip/ – deberían hacer el truco de todos modos (enumera todos los enlaces)

Sé que esto es un poco tonto pero puedes intentar algo como esto:

 pip install django == x 

Esto producirá un error pero mostrará una lista de todas las versiones disponibles para este paquete.

Simplemente reemplace django con el paquete que desea y también creo que, con suerte, no existe tal versión llamada x.

No tuve suerte con yolk , yolk3k o pip install -v pero terminé usando esto (adaptado a Python 3 de la respuesta de eric chiang):

 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("\n".join(versions("gunicorn"))) 19.1.1 19.1.0 19.0.0 18.0 17.5 0.17.4 0.17.3 ... 

Pip 7.1.0 ha eliminado la opción –no instalar desde la instalación. Encontré un método para obtener todas las versiones de un paquete sin ningún paquete adicional.

 $ pip install --no-deps Django==xxx Collecting Django==xxx Could not find a version that satisfies the requirement Django==xxx (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==xxx 

Esto me funciona en OSX:

pip install docker-compose== 2>&1 | grep -oE '(\(.*\))' | awk -F:\ '{print$NF}' | sed -E 's/( |\))//g' | tr ',' '\n'

Devuelve la lista uno por línea:

 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 

O para obtener la última versión disponible:

pip install docker-compose== 2>&1 | grep -oE '(\(.*\))' | awk -F:\ '{print$NF}' | sed -E 's/( |\))//g' | tr ',' '\n' | gsort -r -V | head -1

 1.10.0rc2 

Tenga en cuenta que gsort tiene que estar instalado (en OSX) para analizar las versiones. Puedes instalarlo con brew install coreutils

Puede usar este pequeño script de Python 3 para obtener la lista de versiones disponibles para un paquete de PyPI . A diferencia de otras soluciones de Python publicadas aquí, esto no se rompe en versiones sueltas como 2.2rc1 django o uwsgi de 2.0.17.1 :

 #!/usr/bin/env python import sys import requests from pkg_resources import parse_version def versions(name): """Grab the list of versions for a package from PyPI.""" url = 'https://pypi.python.org/pypi/{}/json'.format(name) return sorted(requests.get(url).json()['releases'], key=parse_version) if __name__ == '__main__': print(*reversed(versions(sys.argv[1])), sep='\n') 

Guarde el script y ejecútelo con el nombre del paquete como argumento, por ejemplo:

 python versions.py django 2.2 2.2rc1 2.2b1 2.2a1 2.1.8 2.1.7 ... 

Una solución alternativa es utilizar las API de Warehouse:

https://warehouse.readthedocs.io/api-reference/json/#release

Por ejemplo para Flask:

 import requests r = requests.get("https://pypi.org/pypi/Flask/json") print(r.json()['releases'].keys()) 

imprimirá:

 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']) 

Acabo de ejecutar esto:

 pip show packagename 

p.ej:

 > 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: 

Aquí está el método actual basado en pip de Python, buscando la API del paquete PyPi heredado:

 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] 

Mi opinión es una combinación de un par de respuestas publicadas, con algunas modificaciones para que sean más fáciles de usar desde un entorno Python en ejecución.

La idea es proporcionar un comando completamente nuevo (modelado después del comando de instalación) que le dé una instancia del buscador de paquetes para usar. La ventaja es que funciona con, y utiliza, cualquier índice que pip admite y lee sus archivos de configuración de pip locales, por lo que obtiene los resultados correctos como lo haría con una instalación pip normal.

Intenté hacerlo compatible con pip v 9.xy 10.x .. pero solo lo probé en 9.x

https://gist.github.com/kaos/68511bd013fcdebe766c981f50b473d4

 #!/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]  [package-index-options] ... %prog [options] [-e]  ... %prog [options] [-e]  ... %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()) 

Ejemplo de salida

 $ ./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 

puedes grep el resultado de tu pip list

 -> % pip list | grep 'beautifulsoup4' beautifulsoup4 (4.4.1)