Comment vérifier si le package python est la dernière version par programme?

29

Comment vérifiez-vous si un package est à sa dernière version par programmation dans un script et retournez un vrai ou un faux?

Je peux vérifier avec un script comme celui-ci:

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

ou avec la ligne de commande:

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

Mais comment puis-je vérifier par programme et retourner vrai ou faux?

Joseph
la source
4
ce n'est pas une solution complète, mais cela pourrait vous donner quelques idées. stackoverflow.com/questions/4888027/…
reyPanda
Pip n'a-t-il pas une API dans laquelle vous pouvez appeler?
Aluan Haddad
3
Si vous pouvez l'utiliser, Python 3.8 a amélioré la prise en charge de ce genre de choses, au moins du côté de ce qui est installé localement . docs.python.org/3/library/importlib.metadata.html
JL Peyret
1
pipn'a pas d'API. Vous voudrez peut-être regarder le pip-apiprojet, mais il n'y en a pas encore beaucoup.
wim

Réponses:

16

Version rapide (vérification de l'emballage uniquement)

Le code ci-dessous appelle le package avec une version non disponible comme pip install package_name==random. L'appel renvoie toutes les versions disponibles. Le programme lit la dernière version.

Le programme s'exécute ensuite pip show package_nameet obtient la version actuelle du package.

S'il trouve une correspondance, il renvoie True, sinon False.

Il s'agit d'une option fiable étant donné qu'elle repose sur 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

Le code suivant appelle 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
la source
Je l'ai mis à jour. Maintenant, il vérifie uniquement le package qui intéresse l'utilisateur. J'ai mis les deux alternatives.
Yusuf Baktir
1
Habituellement, il if (boolean): return True else: return Falsevaut mieux justereturn boolean
wim
Ce n'est pas bon d'utiliser "0" comme un faux numéro de version, car parfois cela va juste aller de l'avant et installer un paquet! (essayez pip install p==0par exemple).
wim
J'ai utilisé, randomje suis sûr qu'il n'y a pas de version aléatoire
Yusuf Baktir
10

Mon projet johnnydepa cette fonctionnalité.

En coquille:

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
la source
Lorsque je l'exécute dans l'invite de commandes Windows, j'obtiens des codes d'échappement ANSI qui rendent le résultat illisible. Je pense que vous voudrez peut-être résoudre ce problème?
user541686
J'ai colorama == 0.4.1 et structlog == 19.2.0, et oui, je vois aussi des codes d'échappement avec cette commande. Si cela aide, j'exécute ceci sur Windows 10.0.17763.195, Python 3.7.4. Je n'ai pas la chance pour le moment de poster un problème malheureusement.
user541686
@ user541686 Il s'agissait du problème 232 , résolu dans structlog v20.1.0 publié aujourd'hui. Merci pour le rapport.
wim
super merci!
user541686
4

Modifier: Supprimer la recherche pip

Merci pour les nombreuses suggestions. Voici une nouvelle version qui n'utilise pas pip searchmais tire à la place la dernière version directement pypicomme proposé par Daniel Hill . Cela résout également le problème avec les fausses correspondances de la sous-chaîne.

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

Réponse originale

Voici une solution rapide qui récupère les informations de dernière version uniquement sur le gekkopackage qui vous intéresse.

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

Cela produit le message Latest version (0.2.3) of gekko is installedet retourne Truepour indiquer la dernière version (ou Falsesinon la dernière version). Ce n'est peut-être pas la meilleure solution car il ne vérifie qu'une sous-chaîne de version if d[name] in s.decode():mais il est plus rapide que pip list --outdatedcela vérifie tous les packages. Ce n'est pas la méthode la plus fiable car elle retournera une erreur Truesi la version installée actuelle est 0.2.3mais la dernière version est 0.2.30ou 0.2.3a. Une amélioration serait d'obtenir par programmation la dernière version et de faire une comparaison directe.

John Hedengren
la source
Attention à pip search. Il utilise une API XML-RPC obsolète, et parfois les résultats de la recherche sont inexacts / incorrects En fait, je pense qu'il pourrait être supprimé bientôt, voir Supprimer la commande de recherche pip # 5216 .
wim
J'ai modifié le code pour utiliser la méthode de Daniel pour extraire la version actuelle du package. Une autre façon d'obtenir la version actuelle de gekko est de faire import gekko, puis current_version=gekko.__version__au lieu de créer un dictionnaire de toutes les versions du package. Cependant, tous les packages n'ont pas un numéro de version accessible dans le package.
John Hedengren
4

Dernière version:

Mon projet ludditea cette fonctionnalité:

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

Version installée:

La manière canonique de vérifier la version installée consiste simplement à accéder à l' __version__attribut de l'espace de noms de niveau supérieur:

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

Malheureusement, tous les projets ne définissent pas cet attribut. Dans le cas contraire, vous pouvez les utiliser pkg_resourcespour les extraire des métadonnées:

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

Cela devrait faire l'affaire au moins à des fins de démonstration. Appelez simplement isLatestVersionavec le nom du colis que vous souhaitez vérifier. Si vous utilisez ceci dans un endroit important, vous voudrez essayer / intercepter la demande d'URL car l'accès Internet peut ne pas être disponible. Notez également que si le package n'est pas installé isLatestVersion, False sera renvoyé.

Ceci est testé pour Python 3.7.4 et 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
la source
1
pip._internaln'est pas une API publique. Il est même explicitement déconseillé dans les documents de pip : " vous ne devez pas utiliser les API internes de pip de cette manière ".
wim
@wim Bon à savoir. Je n'étais pas au courant. Merci de me le faire savoir. Je recommanderais certainement aux gens d'utiliser la méthode de Yusuf Baktir à la place, car c'est plus simple.
Daniel Hill
2

Il n'est pas difficile d'écrire un script simple vous-même en interrogeant l'API PyPI . Avec la dernière version de Python 3.8, il est possible d'utiliser uniquement la bibliothèque standard (lorsque vous utilisez Python 3.7 ou une version antérieure, vous devrez installer le 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)

Exemple d'utilisation:

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

S'il vous arrive d'avoir packaginginstallé, c'est une meilleure alternative à l' distutils.versionanalyse de version:

from distutils.version import LooseVersion

...

LooseVersion(s)

devient

from packaging.version import parse

...

parse(s)
hoefling
la source
Cela peut donner des résultats différents à pip si des index supplémentaires ou alternatifs sont configurés pour l'utilisateur (via le fichier pip.conf ou les vars env PIP_INDEX_URL ou PIP_EXTRA_INDEX_URL)
wim