¿Cómo verificar si el paquete python es la última versión mediante programación?

29

¿Cómo verifica si un paquete está en su última versión mediante programación en un script y devuelve verdadero o falso?

Puedo verificar con un script como este:

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

o con línea de comando:

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

Pero, ¿cómo verifico mediante programación y devuelvo verdadero o falso?

José
fuente
44
No es una solución completa, pero podría darle algunas ideas. stackoverflow.com/questions/4888027/…
reyPanda
¿Pip no tiene una API a la que pueda llamar?
Aluan Haddad el
3
Si puede usarlo, Python 3.8 ha mejorado el soporte para este tipo de cosas, al menos en el lado que está instalado localmente . docs.python.org/3/library/importlib.metadata.html
JL Peyret
1
pipno tiene una API Es posible que desee ver el pip-apiproyecto, pero aún no hay mucho allí.
wim

Respuestas:

16

Versión rápida (solo verificando el paquete)

El siguiente código llama al paquete con una versión no disponible como pip install package_name==random. La llamada devuelve todas las versiones disponibles. El programa lee la última versión.

El programa se ejecuta pip show package_namey obtiene la versión actual del paquete.

Si encuentra una coincidencia, devuelve Verdadero, de lo contrario, Falso.

Esta es una opción confiable dado que está en pie 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

El siguiente código requiere 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
Yusuf Baktir
fuente
Lo he actualizado Ahora verifica solo el paquete que le interesa al usuario. He puesto ambas alternativas.
Yusuf Baktir el
1
Por if (boolean): return True else: return Falselo general, es mejor soloreturn boolean
wim
No es bueno usar "0" como un número de versión falso, porque a veces esto simplemente continuará e instalará un paquete. (prueba pip install p==0por ejemplo).
wim
He usado randomEstoy seguro de que no hay una versión aleatoria
Yusuf Baktir
10

Mi proyecto johnnydeptiene esta característica.

Con cáscara:

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

En Python:

>>> from johnnydep.lib import JohnnyDist
>>> dist = JohnnyDist("gekko")
>>> dist.version_installed  
'0.2.0'
>>> dist.version_latest 
'0.2.3'
wim
fuente
Cuando ejecuto esto en el símbolo del sistema de Windows, obtengo códigos de escape ANSI que hacen que el resultado sea ilegible. Creo que tal vez quieras arreglar esto.
user541686
Tengo colorama == 0.4.1 y structlog == 19.2.0, y sí, también veo códigos de escape con ese comando. Si ayuda, estoy ejecutando esto en Windows 10.0.17763.195, Python 3.7.4. Por el momento, no tengo la oportunidad de publicar un problema.
user541686
@ user541686 Este fue el problema 232 , resuelto en structlog v20.1.0 lanzado hoy. Gracias por el informe
wim
¡genial gracias!
user541686
4

Editar: Eliminar búsqueda de pip

Gracias por las varias sugerencias. Aquí hay una nueva versión que no usa, pip searchsino que extrae la última versión directamente de la pypipropuesta de Daniel Hill . Esto también resuelve el problema con las coincidencias falsas de la subcadena.

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

Respuesta original

Aquí hay una solución rápida que recupera la información de la última versión solo del gekkopaquete de interés.

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

Esto produce el mensaje Latest version (0.2.3) of gekko is installedy vuelve Truea indicar la última versión (o Falsesi no la última versión). Puede que esta no sea la mejor solución porque solo verifica una subcadena de versión, if d[name] in s.decode():pero es más rápida que la pip list --outdatedque verifica todos los paquetes. Este no es el método más confiable porque devolverá un error Truesi la versión actual instalada es 0.2.3pero la última versión es 0.2.30o 0.2.3a. Una mejora sería obtener mediante programación la última versión y hacer una comparación directa.

John Hedengren
fuente
Cuidado con pip search. Utiliza una API XML-RPC en desuso y, a veces, los resultados de búsqueda son inexactos / incorrectos. De hecho, creo que podría eliminarse pronto, consulte Eliminar el comando de búsqueda pip # 5216 .
wim
Modifiqué el código para usar el método de Daniel de extraer la versión actual del paquete. Otra forma de obtener la versión actual de gekko es hacerlo import gekkoy luego, en current_version=gekko.__version__lugar de crear un diccionario de todas las versiones del paquete. Sin embargo, no todos los paquetes tienen un número de versión accesible en el paquete.
John Hedengren
4

Ultima versión:

Mi proyecto ludditetiene esta característica:

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

Versión instalada:

La forma canónica de verificar la versión instalada es simplemente acceder al __version__atributo del espacio de nombres de nivel superior:

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

Lamentablemente, no todos los proyectos establecen este atributo. Cuando no lo hacen, puede usar pkg_resourcespara extraerlo de los metadatos:

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

Esto debería hacer el truco al menos para fines de demostración. Simplemente llame isLatestVersioncon el nombre del paquete que desea verificar. Si está utilizando esto en algún lugar importante, querrá probar / capturar la solicitud de URL, ya que es posible que el acceso a Internet no esté disponible. También tenga en cuenta que si el paquete no está instalado isLatestVersion, devolverá False.

Esto se prueba para Python 3.7.4 y 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
Daniel Hill
fuente
1
pip._internalNo es API pública. Incluso se desaconseja explícitamente en los documentos de pip : " no debe utilizar las API internas de pip de esta manera ".
wim
@wim Es bueno saberlo. No estaba al tanto de esto. Gracias por hacérmelo saber. Definitivamente recomendaría a las personas que usen el método de Yusuf Baktir en su lugar, ya que es más simple.
Daniel Hill
2

No es difícil escribir un script simple usted mismo consultando la API PyPI . Con la última versión de Python 3.8, es posible usar solo la biblioteca estándar (cuando se usa Python 3.7 o anterior, tendrá que instalar el importlib_metadatabackport):

# 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)

Ejemplo de uso:

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

Si tiene packaginginstalado, es una mejor alternativa distutils.versionpara el análisis de versiones:

from distutils.version import LooseVersion

...

LooseVersion(s)

se convierte

from packaging.version import parse

...

parse(s)
hoefling
fuente
Esto puede dar resultados diferentes a pip si se configuran índices adicionales o alternativos para el usuario (a través del archivo pip.conf o PIP_INDEX_URL o PIP_EXTRA_INDEX_URL env vars)
wim