Как проверить, доступен ли пакет с указанной версией? - PullRequest
2 голосов
/ 04 ноября 2019

Например, функция available('foobar>=1.1') должна проверить, что пакет foobar установлен и его версия> = 1.1.

  • Существует importlib.util.find_spec, который может проверить, установлен ли модуль.
  • и LooseVersion из distutils.version для сравнения версий

Но как мне разобрать строку типа foobar>=1.1 и разделить ее на модуль, версию и компаратор?

Или, что более интересно, может быть, уже есть какой-то стандартный способ сделать все эти вещи одновременно? pip, вероятно, делает то же самое, когда читает пакеты из requirements.txt

Ответы [ 3 ]

3 голосов
/ 04 ноября 2019

Вы можете сделать следующее, если это поможет:

import pkg_resources
my_packages = list(pkg_resources.find_distributions("C:/python27/lib/site-packages"))
version_string = my_packages[0].version

In [24]: print(my_packages[0])
zc.buildout 2.9.4
In [25]: my_packages[0]
Out[25]: '2.9.4'

Функциональность требований реализована в pkg_resource в этом довольно большом документе:

https://setuptools.readthedocs.io/en/latest/pkg_resources.html

Например, следующие проверки проверяют, доступны ли пакеты, и предоставляют исключение, если они не

In [31]: pkg_resources.require('zc.buildout == 2.9.4')
Out[31]:
[zc.buildout 2.9.4 (c:\python27\lib\site-packages),
 setuptools 40.6.2 (c:\python27\lib\site-packages)]

. Для анализа имени требований вы можете использовать pkg_resources.parse_requirements, это даст вам версиюи имя модуля и используемый компаратор.

require = list(pkg_resources.parse_requirements("zc.buildout == 2.9.4"))[0]
print(require.name)
'zc.buildout'
print(require.specs)
 [('==', '2.9.4')]

Для функции, которую вы хотите написать, вы можете сделать следующее: Обратите внимание, что это не проверяет, доступен ли пакет.

def split_package_requirement(package_string='foobar>=1.1'):
    """Splits the requirement into, name, comparator and version."""
    requirement = next(pkg_resources.parse_requirements("zc.buildout == 2.9.4"))
    comparator, version = requirement.specs[0]

    return requirement.name, comparator, version
0 голосов
/ 04 ноября 2019

Если вы не возражаете против использования модуля подпроцесса (вопросы безопасности) Существует способ, который зависит от вывода списка пипсов.

Простой способ проверить существование и минимальную версию

import sys
import subprocess
from distutils.version import LooseVersion

def available(desired_package, min_version):
    packages = subprocess.check_output([sys.executable, '-m', 'pip', 'list']).decode().split('\n')[2:-1] # First 2 lines, and last line are not packages
    package_version = [package.split() for package in packages]

    package_dict = {}
    for package, version in package_version:
        package_dict[package] = version

    try: 
        if LooseVersion(package_dict[desired_package]) >= LooseVersion(min_version):
            print('Minimum version requirement met')
        else:
            print('Minimum version requirement not met')
    except KeyError:
            print('{} not found.'.format(desired_package))
>>> available('tensorflow', '0.0.1')
Minimum version requirement met
>>> available('tensorflow', '20.0.1')
Minimum version requirement not met
>>> available('tensorflowasd', '20.0.1')
tensorflowasd not found.

Более сложный способ, если необходимо выполнить точное сравнение с входа: «tenorflow> 1.13.0"

import sys
import subprocess
from distutils.version import LooseVersion
import re

def available(input):
    def version_comparer(version, desired_version, comparator):
        if comparator == '<=': return LooseVersion(version) <= LooseVersion(desired_version)
        elif comparator == '>=': return LooseVersion(version) >= LooseVersion(desired_version)
        elif comparator == '==': return LooseVersion(version) == LooseVersion(desired_version)
        elif comparator == '>': return LooseVersion(version) > LooseVersion(desired_version)
        elif comparator == '<': return LooseVersion(version) < LooseVersion(desired_version)
        else:
            print('Unexpected comparator')
            exit(1)

    match = re.match('([A-Za-z0-9-_]+)([^A-Za-z0-9-_]+)([\d\.]+$)', input)
    desired_package, comparator, desired_version = match[1], match[2], match[3]

    packages = subprocess.check_output([sys.executable, '-m', 'pip', 'list']).decode().split('\n')[2:-1] # First 2 lines, and last line are not packages
    package_version = [package.split() for package in packages]

    package_dict = {}
    for package, version in package_version:
        package_dict[package] = version
    try: 
        if version_comparer(package_dict[desired_package], desired_version, comparator):
            print('Input validated')
        else:
            print('Package exists, but requirement not met')
    except KeyError:
        print('{} not found.'.format(desired_package))
>>> available('tensorflow<0.0.1')
Package exists, but requirement not met
>>> available('tensorflowasd<0.0.1')
tensorflowasd not found.
>>> available('tensorflow>0.0.1')
Input validated
0 голосов
/ 04 ноября 2019

Вы можете использовать что-то вроде этого для анализа имени и версии модуля

line = 'foobar>=1.1'
def f(line):

  i = line.find('=')
  if i!=-1:
    if line[i+1]!='=':
        i=i-1
    return line[:i],line[i+2:]
  else:
    return line,''

Вывод:

('foobar', '1.1')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...