Проверка версии модуля Python во время выполнения

Многие сторонние модули Python имеют атрибут, который содержит информацию о версии для модуля (обычно это что-то вроде module.VERSION или module.__version__), однако некоторые из них этого не делают.

Частными примерами таких модулей являются libxslt и libxml2.

Мне нужно проверить, что правильная версия этих модулей используется во время выполнения. Есть ли способ сделать это?

Потенциальное решение wold должно быть прочитано в источнике во время выполнения, хеш его, а затем сравнить его с хэшем известной версии, но это противно.

Есть ли лучшие решения?

Ответ 1

Я бы держался подальше от хеширования. В используемой версии libxslt может содержаться некоторый тип патча, который не влияет на его использование.

В качестве альтернативы я бы хотел предложить, чтобы вы не проверяли во время выполнения (не знаете, жестко это или нет). Для содержимого python я пишу, что имеет внешние зависимости (сторонние библиотеки), пишу script, который пользователи могут запускать, чтобы проверить их установку python, чтобы проверить, установлены ли соответствующие версии модулей.

Для модулей, у которых нет определенного атрибута "версия", вы можете проверить интерфейсы, которые он содержит (классы и методы), и посмотреть, соответствуют ли они интерфейсу, который они ожидают. Затем в действительном коде, над которым вы работаете, предположите, что сторонние модули имеют интерфейс, который вы ожидаете.

Ответ 2

Используйте pkg_resources. Все, что установлено из PyPI, должно иметь номер версии.

>>> import pkg_resources
>>> pkg_resources.get_distribution("blogofile").version
'0.7.1'

Ответ 3

Некоторые идеи:

  • Попробуйте проверить функции, которые существуют или не существуют в ваших необходимых версиях.
  • Если функциональных различий нет, проверьте аргументы функции и подписи.
  • Если вы не можете понять это из сигнатур функций, настройте некоторые заглушки во время импорта и проверьте их поведение.

Ответ 4

Вы можете использовать

pip freeze

чтобы увидеть установленные пакеты в формате требований.

Ответ 5

Для этого вы можете использовать библиотеку importlib_metadata.

Если вы используете Python < 3.8, сначала установите его с помощью:

pip install importlib_metadata

Начиная с Python 3.8 он входит в стандартную библиотеку Python.

Затем, чтобы проверить версию пакета (в этом примере lxml), запустите:

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

Имейте в виду, что это работает только для пакетов, установленных из PyPI. Кроме того, вы должны передать имя пакета в качестве аргумента методу version, а не имя модуля, которое предоставляет этот пакет (хотя обычно они совпадают).

Ответ 6

Я нашел довольно ненадежным использование различных доступных инструментов (включая лучший pkg_resources упомянутый в этом другом ответе), так как большинство из них не охватывает все случаи. Например

  • встроенные модули
  • модули не установлены, а просто добавлены в путь к Python (например, вашей IDE)
  • доступны две версии одного и того же модуля (одна в пути Python заменяет установленную)

Так как нам нужен был надежный способ получить версию любого пакета, модуля или субмодуля, я закончил писать getversion. Это довольно просто использовать:

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

Смотрите документацию для деталей.

Ответ 7

Для модулей, которые не предоставляют __version__, это ужасно, но работает:

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

или

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