Come verificare se il pacchetto python è l'ultima versione a livello di codice?


29

Come si controlla se un pacchetto è programmaticamente nella sua ultima versione in uno script e restituisce un vero o falso?

Posso verificare con uno script come questo:

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 riga di comando:

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

Ma come posso controllare a livello di codice e restituire vero o falso?


4
non è una soluzione completa ma potrebbe darti alcune idee. stackoverflow.com/questions/4888027/...
reyPanda

Pip non ha un API in cui puoi chiamare?
Aluan Haddad,

3
Se puoi usarlo, Python 3.8 ha migliorato il supporto per questo genere di cose, almeno sul lato installato localmente . docs.python.org/3/library/importlib.metadata.html
JL Peyret

1
pipnon ha un'API. Potresti voler guardare il pip-apiprogetto, ma non c'è ancora molto.
mercoledì

Risposte:


16

Versione veloce (controllo solo del pacchetto)

Il codice seguente chiama il pacchetto con una versione non disponibile come pip install package_name==random. La chiamata restituisce tutte le versioni disponibili. Il programma legge l'ultima versione.

Il programma viene quindi eseguito pip show package_namee ottiene la versione corrente del pacchetto.

Se trova una corrispondenza, restituisce True, altrimenti False.

Questa è un'opzione affidabile dato che è valida 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

Il seguente codice richiede 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

L'ho aggiornato. Ora controlla solo il pacchetto a cui l'utente è interessato. Ho inserito entrambe le alternative.
Yusuf Baktir,

1
Di solito if (boolean): return True else: return Falseè meglio soloreturn boolean
wim

Non è buono usare "0" come numero di versione fasullo, perché a volte questo andrà avanti e installerà un pacchetto! (prova pip install p==0ad esempio).
mercoledì

Ho usato randomsono sicuro che non esiste una versione casuale
Yusuf Baktir,

10

Il mio progetto johnnydepha questa funzione.

Nel guscio:

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

In Python:

>>> from johnnydep.lib import JohnnyDist
>>> dist = JohnnyDist("gekko")
>>> dist.version_installed  
'0.2.0'
>>> dist.version_latest 
'0.2.3'

Quando eseguo questo nel prompt dei comandi di Windows, ottengo i codici di escape ANSI che rendono il risultato illeggibile. Penso che potresti voler risolvere questo?
user541686

Ho colorama == 0.4.1 e structlog == 19.2.0, e sì, vedo anche i codici di escape con quel comando. Se aiuta sto eseguendo questo su Windows 10.0.17763.195, Python 3.7.4. Al momento non ho la possibilità di pubblicare un problema.
user541686

@ user541686 Questo è stato issue232 , risolto in structlog v20.1.0 rilasciato oggi. Grazie per la segnalazione
mercoledì

fantastico, grazie!
user541686

4

Modifica: rimuove la ricerca pip

Grazie per i numerosi suggerimenti. Ecco una nuova versione che non utilizza pip searchma estrae invece l'ultima versione direttamente da pypicome proposto da Daniel Hill . Ciò risolve anche il problema con le corrispondenze false della sottostringa.

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

Risposta originale

Ecco una soluzione rapida che recupera le informazioni sulla versione più recente solo sul gekkopacchetto di interesse.

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

Questo produce il messaggio Latest version (0.2.3) of gekko is installede ritorna Trueper indicare la versione più recente (o Falsese non la versione più recente). Questa potrebbe non essere la soluzione migliore perché controlla solo la versione di una sottostringa if d[name] in s.decode():ma è più veloce di pip list --outdatedquella che controlla tutti i pacchetti. Questo non è il metodo più affidabile perché restituirà un errore Truese l'attuale versione installata è 0.2.3ma l'ultima versione è 0.2.30o 0.2.3a. Un miglioramento sarebbe ottenere a livello di codice l'ultima versione e fare un confronto diretto.


Attento con pip search. Utilizza un'API XML-RPC obsoleta e, a volte, i risultati della ricerca sono imprecisi / errati. In effetti, penso che potrebbe essere rimosso presto, vedere Rimuovere il comando di ricerca pip # 5216 .
mercoledì

Ho modificato il codice per utilizzare il metodo di Daniel per estrarre l'attuale versione del pacchetto. Un altro modo per ottenere l'attuale versione di gekko è fare import gekkoe quindi current_version=gekko.__version__invece di creare un dizionario di tutte le versioni del pacchetto. Tuttavia, non tutti i pacchetti hanno un numero di versione accessibile nel pacchetto.
John Hedengren,

4

Ultima versione:

Il mio progetto ludditeha questa funzione:

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

Versione installata:

Il modo canonico di controllare la versione installata è solo per accedere __version__all'attributo dello spazio dei nomi di livello superiore:

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

Purtroppo non tutti i progetti impostano questo attributo. In caso contrario, è possibile utilizzare pkg_resourcesper estrarlo dai metadati:

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

2

Questo dovrebbe fare il trucco almeno per scopi dimostrativi. Chiama semplicemente isLatestVersioncon il nome del pacchetto che desideri controllare. Se lo stai usando in un posto importante, dovresti provare a catturare la richiesta url poiché l'accesso a Internet potrebbe non essere disponibile. Si noti inoltre che se il pacchetto non è installato isLatestVersionrestituirà False.

Questo è testato per Python 3.7.4 e 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

1
pip._internalnon è un'API pubblica. È persino esplicitamente scoraggiato nei documenti di pip : " non devi usare le API interne di pip in questo modo ".
mercoledì

@wim Buono a sapersi. Non ne ero a conoscenza. Grazie per avermi fatto sapere. Consiglierei sicuramente alle persone che usano il metodo di Yusuf Baktir invece in quanto è più semplice.
Daniel Hill,

2

Non è difficile scrivere un semplice script eseguendo una query sull'API PyPI . Con l'ultimo Python 3.8, è possibile utilizzare solo la libreria standard (quando si utilizza Python 3.7 o precedente, è necessario installare il 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)

Esempio di utilizzo:

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

Se ti capita di aver packaginginstallato, è un'alternativa migliore distutils.versionper l'analisi della versione:

from distutils.version import LooseVersion

...

LooseVersion(s)

diventa

from packaging.version import parse

...

parse(s)

Questo può dare diversi risultati al pip se gli indici aggiuntivi o alternativi sono configurati per l'utente (tramite il file pip.conf o PIP_INDEX_URL o PIP_EXTRA_INDEX_URL env vars)
wim
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.