Sprawdzanie wersji modułu Pythona w czasie wykonywania

118

Wiele modułów Pythona innych firm ma atrybut, który przechowuje informacje o wersji modułu (zwykle coś w rodzaju module.VERSIONlub module.__version__), jednak niektóre go nie mają.

Konkretnymi przykładami takich modułów są libxslt i libxml2.

Muszę sprawdzić, czy w czasie wykonywania jest używana poprawna wersja tych modułów. Czy jest na to sposób?

Potencjalnym rozwiązaniem byłoby odczytanie źródła w czasie wykonywania, zaszyfrowanie go, a następnie porównanie z hashem znanej wersji, ale to paskudne.

Czy są lepsze rozwiązania?

Gburowaty
źródło

Odpowiedzi:

8

Trzymałbym się z dala od haszowania. Używana wersja libxslt może zawierać jakiś rodzaj łatki, która nie wpływa na jej użycie.

Alternatywnie chciałbym zasugerować, aby nie sprawdzać w czasie wykonywania (nie wiem, czy to trudne wymaganie, czy nie). W przypadku rzeczy o Pythonie, które piszę, które mają zewnętrzne zależności (biblioteki innych firm), piszę skrypt, który użytkownicy mogą uruchomić, aby sprawdzić instalację Pythona, aby zobaczyć, czy są zainstalowane odpowiednie wersje modułów.

W przypadku modułów, które nie mają zdefiniowanego atrybutu „wersja”, możesz sprawdzić zawarte w nim interfejsy (klasy i metody) i sprawdzić, czy pasują do oczekiwanego interfejsu. Następnie we właściwym kodzie, nad którym pracujesz, załóż, że moduły innych firm mają interfejs, którego oczekujesz.

Mark Roddy
źródło
244

Użyj pkg_resources . Wszystko, co zostało zainstalowane z PyPI, powinno mieć przynajmniej numer wersji.

>>> import pkg_resources
>>> pkg_resources.get_distribution("blogofile").version
'0.7.1'
EnigmaCurry
źródło
8
Należy również pamiętać, że nazwa pakietu musi być nazwą wpisu PyPI. Więc coś takiego jak „pkg_resources.get_distribution ('MySQLdb'). Version” nie będzie działać, ale „pkg_resources.get_distribution ('mysql-python'). Version” będzie działać.
Rahul
1
Jeśli korzystasz z bezwzględnej nazwy pliku, możesz pkg_resourceswybrać inną wersję, cieniując tę, którą aktualnie używasz, ponieważ ma ona wyższy priorytet nad Twoją PYTHONPATHlub podobną.
tripleee
4
W przypadku, gdy ktoś chce wiedzieć, jak utworzyć __version__atrybut: stackoverflow.com/q/17583443/562769
Martin Thoma
pkg_resourceslink to błąd 404
gerrit,
W przypadku automatycznego przetwarzania zobacz to pytanie
gerrit
6

Jakieś pomysły:

  1. Spróbuj poszukać funkcji, które istnieją lub nie istnieją w Twoich potrzebnych wersjach.
  2. Jeśli nie ma różnic w funkcjach, sprawdź argumenty i podpisy funkcji.
  3. Jeśli nie możesz tego rozgryźć na podstawie sygnatur funkcji, skonfiguruj kilka wywołań pośrednich w czasie importu i sprawdź ich zachowanie.
Richard Levasseur
źródło
Pakiety powinny określać swoją wersję. Te pomysły są całkowicie przesadą w przypadku zwykle (szacunkowo 99% przypadków) łatwego zadania sprawdzania wersji.
Zelphir Kaltstahl
2

Możesz użyć

pip freeze

aby zobaczyć zainstalowane pakiety w formacie wymagań.

nr
źródło
1

Możesz importlib_metadatado tego użyć biblioteki.

Jeśli korzystasz z Pythona < 3.8, najpierw zainstaluj go za pomocą:

pip install importlib_metadata

Ponieważ python 3.8jest zawarty w standardowej bibliotece Pythona.

Następnie, aby sprawdzić wersję pakietu (w tym przykładzie lxml), wykonaj:

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

Należy pamiętać, że działa to tylko dla pakietów zainstalowanych z PyPI. Ponadto musisz przekazać nazwę pakietu jako argument versionmetody, a nie nazwę modułu, który ten pakiet zapewnia (chociaż zwykle są takie same).

Jakub Kukul
źródło
0

Uważam, że używanie różnych dostępnych narzędzi (w tym najlepszego pkg_resourceswymienionego w tej drugiej odpowiedzi ) jest dość zawodne , ponieważ większość z nich nie obejmuje wszystkich przypadków. Na przykład

  • wbudowane moduły
  • moduły nie są zainstalowane, ale właśnie dodane do ścieżki Pythona (na przykład przez IDE)
  • dostępne dwie wersje tego samego modułu (jedna w ścieżce Pythona zastępuje zainstalowaną)

Ponieważ potrzebowaliśmy niezawodnego sposobu na uzyskanie wersji dowolnego pakietu, modułu lub podmodułu, napisałem getversion . Jest dość prosty w użyciu:

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

Szczegółowe informacje można znaleźć w dokumentacji .

smarie
źródło
0

Dla modułów, które nie zapewniają __version__następujących elementów, jest brzydki, ale działa:

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

lub

#!/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
źródło