Wie kann ich programmgesteuert überprüfen, ob das Python-Paket die neueste Version ist?

29

Wie überprüfen Sie programmgesteuert in einem Skript, ob ein Paket die neueste Version hat, und geben ein wahres oder falsches zurück?

Ich kann mit einem Skript wie diesem überprüfen:

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

oder mit Kommandozeile:

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

Aber wie überprüfe ich programmgesteuert und gebe true oder false zurück?

Joseph
quelle
4
keine vollständige Lösung, aber es könnte Ihnen einige Ideen geben. stackoverflow.com/questions/4888027/…
reyPanda
Hat Pip keine API, in die du anrufen kannst?
Aluan Haddad
3
Wenn Sie davon Gebrauch machen können, hat Python 3.8 die Unterstützung für diese Art von Dingen verbessert, zumindest auf der lokal installierten Seite. docs.python.org/3/library/importlib.metadata.html
JL Peyret
1
piphat keine API. Vielleicht möchten Sie sich das pip-apiProjekt ansehen , aber es ist noch nicht viel da.
wim

Antworten:

16

Schnelle Version (nur das Paket überprüfen)

Der folgende Code ruft das Paket mit einer nicht verfügbaren Version wie auf pip install package_name==random. Der Aufruf gibt alle verfügbaren Versionen zurück. Das Programm liest die neueste Version.

Das Programm wird dann ausgeführt pip show package_nameund erhält die aktuelle Version des Pakets.

Wenn eine Übereinstimmung gefunden wird, wird True zurückgegeben, andernfalls False.

Dies ist eine zuverlässige Option, da sie aktiviert ist 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

Der folgende Code erfordert 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
quelle
Ich habe es aktualisiert. Jetzt wird nur das Paket überprüft, an dem der Benutzer interessiert ist. Ich habe beide Alternativen angegeben.
Yusuf Baktir
1
Normalerweise if (boolean): return True else: return Falseist besser , nur zureturn boolean
wim
Es ist nicht gut, "0" als falsche Versionsnummer zu verwenden, da dies manchmal einfach vorgeht und ein Paket installiert! (versuchen Sie es pip install p==0zum Beispiel).
wim
Ich habe verwendet, randomich bin sicher, es gibt keine zufällige Version
Yusuf Baktir
10

Mein Projekt johnnydephat diese Funktion.

In der Schale:

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'
wim
quelle
Wenn ich dies in der Windows-Eingabeaufforderung ausführe, erhalte ich ANSI-Escape-Codes, die das Ergebnis unlesbar machen. Ich denke, Sie möchten das vielleicht beheben?
user541686
Ich habe colorama == 0.4.1 und structlog == 19.2.0, und ja, ich sehe auch Escape-Codes mit diesem Befehl. Wenn es hilft, führe ich dies unter Windows 10.0.17763.195, Python 3.7.4 aus. Ich habe momentan leider keine Chance, ein Problem zu posten.
user541686
@ user541686 Dies war das Problem232 , das in der heute veröffentlichten Struktur v20.1.0 behoben wurde. Danke für den Bericht.
wim
super, danke!
user541686
4

Bearbeiten: Pip-Suche entfernen

Vielen Dank für die verschiedenen Vorschläge. Hier ist eine neue Version, die nicht verwendet, pip searchsondern die neueste Version direkt pypivon Daniel Hill abruft . Dies behebt auch das Problem mit den falschen Übereinstimmungen der Teilzeichenfolge.

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

Ursprüngliche Antwort

Hier ist eine schnelle Lösung, die die neuesten Versionsinformationen nur für das gekkointeressierende Paket abruft .

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

Dies erzeugt die Nachricht Latest version (0.2.3) of gekko is installedund kehrt zurück True, um die neueste Version (oder Falsewenn nicht die neueste Version) anzugeben . Dies ist möglicherweise nicht die beste Lösung, da nur nach einem Versions-Teilstring gesucht wird, if d[name] in s.decode():aber schneller als pip list --outdatedalle Pakete überprüft werden. Dies ist nicht die zuverlässigste Methode, da sie eine falsche zurückgibt, Truewenn die aktuell installierte Version 0.2.3die neueste Version ist 0.2.30oder 0.2.3a. Eine Verbesserung wäre, programmgesteuert die neueste Version zu erhalten und einen direkten Vergleich durchzuführen.

John Hedengren
quelle
Vorsicht mit pip search. Es verwendet eine veraltete XML-RPC-API, und manchmal sind die Suchergebnisse ungenau / falsch. Tatsächlich denke ich, dass sie möglicherweise bald entfernt werden, siehe Entfernen des Pip-Suchbefehls # 5216 .
wim
Ich habe den Code geändert, um Daniels Methode zum Abrufen der aktuellen Paketversion zu verwenden. Eine weitere Möglichkeit , die aktuelle gekko - Version zu erhalten , ist zu tun , import gekkound dann current_version=gekko.__version__statt ein Wörterbuch aller Paketversionen zu erstellen. Allerdings haben nicht alle Pakete eine Versionsnummer, auf die im Paket zugegriffen werden kann.
John Hedengren
4

Letzte Version:

Mein Projekt ludditehat diese Funktion:

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

Installierte Version:

Die kanonische Möglichkeit, die installierte Version zu überprüfen, besteht darin, auf das __version__Attribut des Namespace der obersten Ebene zuzugreifen :

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

Leider setzen nicht alle Projekte dieses Attribut. Wenn dies nicht der Fall ist, können pkg_resourcesSie es aus den Metadaten ausgraben:

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

Dies sollte zumindest für Demozwecke ausreichen. Rufen Sie einfach isLatestVersionmit dem Namen des Pakets an, das Sie überprüfen möchten. Wenn Sie dies an einem wichtigen Ort verwenden, möchten Sie versuchen, die URL-Anfrage abzufangen, da der Internetzugang möglicherweise nicht verfügbar ist. Beachten Sie auch, dass isLatestVersionFalse zurückgegeben wird , wenn das Paket nicht installiert ist.

Dies wird für Python 3.7.4 und Pip 19.0.3 getestet.

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
quelle
1
pip._internalist keine öffentliche API. In den Dokumenten von pip wird sogar ausdrücklich davon abgeraten : " Sie dürfen die internen APIs von pip nicht auf diese Weise verwenden. "
wim
@wim Gut zu wissen. Das war mir nicht bewusst. Danke für die Information. Ich würde definitiv empfehlen, stattdessen die Methode von Yusuf Baktir zu verwenden, da diese einfacher ist.
Daniel Hill
2

Es ist nicht schwer, selbst ein einfaches Skript zu schreiben, indem Sie die PyPI-API abfragen . Mit dem neuesten Python 3.8 ist es möglich, nur die Standardbibliothek zu verwenden (wenn Sie Python 3.7 oder älter verwenden, müssen Sie den importlib_metadataBackport installieren ):

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

Anwendungsbeispiel:

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

Wenn Sie zufällig packaginginstalliert haben, ist dies eine bessere Alternative zum distutils.versionParsen von Versionen:

from distutils.version import LooseVersion

...

LooseVersion(s)

wird

from packaging.version import parse

...

parse(s)
hoefling
quelle
Dies kann zu unterschiedlichen Ergebnissen für pip führen, wenn zusätzliche oder alternative Indizes für den Benutzer konfiguriert sind (über die Datei pip.conf oder PIP_INDEX_URL oder PIP_EXTRA_INDEX_URL env vars)
wim