Überprüfen einer Python-Modulversion zur Laufzeit

118

Viele Python-Module von Drittanbietern verfügen über ein Attribut, das die Versionsinformationen für das Modul enthält (normalerweise so etwas wie module.VERSIONoder module.__version__), einige jedoch nicht.

Besondere Beispiele für solche Module sind libxslt und libxml2.

Ich muss überprüfen, ob zur Laufzeit die richtige Version dieser Module verwendet wird. Gibt es eine Möglichkeit, dies zu tun?

Eine mögliche Lösung wäre, die Quelle zur Laufzeit einzulesen, zu hashen und dann mit dem Hash der bekannten Version zu vergleichen, aber das ist böse.

Gibt es bessere Lösungen?

Schroff
quelle

Antworten:

8

Ich würde mich vom Hashing fernhalten. Die verwendete Version von libxslt enthält möglicherweise eine Art Patch, der sich nicht auf Ihre Verwendung auswirkt.

Als Alternative möchte ich vorschlagen, dass Sie nicht zur Laufzeit prüfen (ich weiß nicht, ob dies eine schwierige Anforderung ist oder nicht). Für das Python-Material, das ich schreibe und das externe Abhängigkeiten aufweist (Bibliotheken von Drittanbietern), schreibe ich ein Skript, das Benutzer ausführen können, um ihre Python-Installation zu überprüfen, um festzustellen, ob die entsprechenden Versionen von Modulen installiert sind.

Für die Module, für die kein Attribut 'version' definiert ist, können Sie die darin enthaltenen Schnittstellen (Klassen und Methoden) überprüfen und feststellen, ob sie mit der erwarteten Schnittstelle übereinstimmen. Nehmen Sie dann im eigentlichen Code, an dem Sie arbeiten, an, dass die Module von Drittanbietern die Schnittstelle haben, die Sie erwarten.

Mark Roddy
quelle
244

Verwenden Sie pkg_resources . Alles, was von PyPI installiert wird, sollte mindestens eine Versionsnummer haben.

>>> import pkg_resources
>>> pkg_resources.get_distribution("blogofile").version
'0.7.1'
EnigmaCurry
quelle
8
Beachten Sie auch, dass der Paketname der des PyPI-Eintrags sein muss. So etwas wie "pkg_resources.get_distribution ('MySQLdb'). Version" funktioniert nicht, aber "pkg_resources.get_distribution ('mysql-python'). Version" wird funktionieren.
Rahul
1
Wenn Sie mit einem absoluten Dateinamen ausgeführt werden, wird pkg_resourcesmöglicherweise eine andere Version verwendet, die die tatsächlich ausgeführte Version überschattet, da sie eine höhere Priorität für Ihre PYTHONPATHoder eine ähnliche Version hat .
Tripleee
4
Für den Fall, dass jemand wissen möchte, wie das __version__Attribut erstellt wird: stackoverflow.com/q/17583443/562769
Martin Thoma
pkg_resourcesLink ist ein Fehler 404
Gerrit
Informationen zur
6

Einige Ideen:

  1. Versuchen Sie, nach Funktionen zu suchen, die in Ihren benötigten Versionen vorhanden sind oder nicht.
  2. Wenn es keine Funktionsunterschiede gibt, überprüfen Sie die Funktionsargumente und Signaturen.
  3. Wenn Sie dies anhand von Funktionssignaturen nicht herausfinden können, richten Sie beim Import einige Stub-Aufrufe ein und überprüfen Sie deren Verhalten.
Richard Levasseur
quelle
Pakete sollten ihre Version angeben. Diese Ideen sind für die normalerweise (geschätzte 99% der Fälle) einfache Aufgabe, eine Version zu überprüfen, völlig übertrieben.
Zelphir Kaltstahl
2

Sie können verwenden

pip freeze

um die installierten Pakete im Anforderungsformat anzuzeigen.

nr
quelle
1

Sie können hierfür die importlib_metadataBibliothek verwenden.

Wenn Sie auf Python <sind 3.8, installieren Sie es zuerst mit:

pip install importlib_metadata

Seit Python ist 3.8es in der Standardbibliothek von Python enthalten.

Um dann die Version eines Pakets (in diesem Beispiel lxml) zu überprüfen, führen Sie Folgendes aus :

>>> from importlib_metadata import version
>>> version('lxml')
'4.3.1'

Beachten Sie, dass dies nur für Pakete funktioniert, die von PyPI installiert wurden. Außerdem müssen Sie der versionMethode einen Paketnamen als Argument übergeben und nicht einen Modulnamen, den dieses Paket bereitstellt (obwohl diese normalerweise identisch sind).

Jakub Kukul
quelle
0

Ich fand es ziemlich unzuverlässig, die verschiedenen verfügbaren Tools zu verwenden (einschließlich des besten, pkg_resourcesdas in dieser anderen Antwort erwähnt wird ), da die meisten nicht alle Fälle abdecken. Beispielsweise

  • eingebaute Module
  • Module nicht installiert, sondern nur zum Python-Pfad hinzugefügt (z. B. von Ihrer IDE)
  • zwei Versionen desselben Moduls verfügbar (eine im Python-Pfad ersetzt die installierte)

Da wir einen zuverlässigen Weg , um die Version des benötigten jedes Paket, Modul oder Submodul, landete ich schriftlich bis getversion . Es ist ganz einfach zu bedienen:

from getversion import get_module_version
import foo
version, details = get_module_version(foo)

Einzelheiten finden Sie in der Dokumentation .

smarie
quelle
0

Für Module, __version__die Folgendes nicht bieten, ist dies hässlich, funktioniert aber:

#!/usr/bin/env python3.6
import sys
import os
import subprocess
import re

sp = subprocess.run(["pip3", "show", "numpy"], stdout=subprocess.PIPE)
ver = sp.stdout.decode('utf-8').strip().split('\n')[1]
res = re.search('^Version:\ (.*)$', ver)
print(res.group(1))

oder

#!/usr/bin/env python3.7
import sys
import os
import subprocess
import re

sp = subprocess.run(["pip3", "show", "numpy"], capture_output=True)
ver = sp.stdout.decode('utf-8').strip().split('\n')[1]
res = re.search('^Version:\ (.*)$', ver)
print(res.group(1))
phzx_munki
quelle