Як перевірити, чи програмний пакет python є останньою версією програмно?


29

Як перевірити, чи пакується пакет із останньою версією програмно у сценарії та повернути справжній чи хибний?

Я можу перевірити такий сценарій:

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

або з командним рядком:

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

Але як я перевіряю програмно і повертаю істинне чи хибне?


4
не повне рішення, але це може дати вам кілька ідей. stackoverflow.com/questions/4888027 / ...
reyPanda

У pip немає api, до якого можна зателефонувати?
Алуан Хаддад

3
Якщо ви можете скористатись цим, Python 3.8 покращив підтримку цього роду, принаймні щодо того, що встановлено локально . docs.python.org/3/library/importlib.metadata.html
JL

1
pipне має API. Можливо, ви хочете подивитися pip-apiпроект, але його ще мало.
Вім

Відповіді:


16

Швидка версія (Перевірка пакета)

Нижче наведений код називає пакунок такою недоступною версією, як pip install package_name==random. Виклик повертає всі наявні версії. Програма читає останню версію.

Потім програма запускається pip show package_nameта отримує поточну версію пакета.

Якщо він знайде збіг, він повертає True, інакше False.

Це надійний варіант, враховуючи, на якому він стоїть 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

Наступний код вимагає 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

Я оновив його. Тепер він перевіряє лише той пакет, який цікавить користувача. Я поставив обидві альтернативи.
Юсуф Бактір

1
Зазвичай if (boolean): return True else: return Falseкраще простоreturn boolean
WIM

Недобре використовувати "0" як фіктивний номер версії, адже іноді це просто піде вперед і встановить пакет! (спробуйте, pip install p==0наприклад).
Вім

Я використав, randomя впевнений, що випадкової версії немає
Юсуф Бактір

10

Мій проект johnnydepмає таку особливість.

У оболонці:

pip install --upgrade pip johnnydep
pip install gekko==0.2.0

На Python:

>>> from johnnydep.lib import JohnnyDist
>>> dist = JohnnyDist("gekko")
>>> dist.version_installed  
'0.2.0'
>>> dist.version_latest 
'0.2.3'

Коли я запускаю це в командному рядку Windows, я отримую ANSI коди евакуації, які роблять результат нечитабельним. Я думаю, ви, можливо, захочете це виправити?
користувач541686

У мене є колорама == 0.4.1 і структура = 19.2.0, і так, я також бачу коди втечі з цією командою. Якщо це допоможе, я запускаю це в Windows 10.0.17763.195, Python 3.7.4. На жаль, наразі у мене немає можливості опублікувати випуск.
користувач541686

@ user541686 Це було проблемою232 , вирішено у випущеній сьогодні структурі v20.1.0. Дякуємо за звіт.
Вім

приголомшливий, дякую!
користувач541686

4

Редагувати: Видалити пошук у файлі

Дякую за кілька пропозицій. Ось нова версія, яка не використовується, pip searchа натомість витягує останню версію безпосередньо із pypiзапропонованої Даніелем Хіллом . Це також вирішує проблему з підрядними помилковими збігами.

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

Оригінальна відповідь

Ось швидке рішення, яке отримує інформацію про останню версію лише gekkoпро цікавий пакет.

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

Це створює повідомлення Latest version (0.2.3) of gekko is installedта повертається Trueіз зазначенням останньої версії (або, Falseякщо не останньої). Це може бути не найкращим рішенням, оскільки воно перевіряє лише підстрокову версію, if d[name] in s.decode():але це швидше, ніж pip list --outdatedце перевіряє всі пакети. Це не найнадійніший метод, оскільки він поверне неправильну, Trueякщо поточна встановлена ​​версія є, 0.2.3але остання версія є 0.2.30або 0.2.3a. Удосконаленням було б програмно отримати останню версію та зробити пряме порівняння.


Обережно з pip search. Він використовує застарілий API XML-RPC, а іноді результати пошуку є неточними / неправильними. Насправді, я думаю, це може бути видалено незабаром, див. Видалити команду пошуку pip # 5216 .
Вім

Я змінив код, щоб використати метод Даніеля, який витягує поточну версію пакета. Ще один спосіб отримати поточну версію gekko - це зробити, import gekkoа потім current_version=gekko.__version__замість створення словника всіх версій пакету. Однак, не всі пакунки мають номер версії, доступний в пакеті.
Джон Хеденгрен

4

Остання версія:

Мій проект ludditeмає таку особливість:

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

Встановлена ​​версія:

Канонічний спосіб перевірити встановлену версію - це лише отримати доступ до __version__атрибуту простору імен верхнього рівня:

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

На жаль, не всі проекти встановлюють цей атрибут. Якщо цього немає, ви можете pkg_resourcesвиконувати його з метаданих:

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

2

Це повинно зробити трюк хоча б для демонстраційних цілей. Просто зателефонуйтеisLatestVersion з назвою пакету, який ви хочете перевірити. Якщо ви використовуєте це десь важливо, ви хочете спробувати ввести запит на URL, оскільки доступ до Інтернету може бути недоступним. Також врахуйте, що якщо пакет не встановленийisLatestVersion , повернеться помилково.

Це перевірено на Python 3.7.4 та Pip 19.0.3.

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

1
pip._internalне є загальнодоступним API. Це навіть явно не відволікається в документах pip : " Ви не повинні використовувати внутрішні API Pip таким чином ".
Вім

@wim Добре знати. Я цього не знав. Дякую, що повідомив. Я б напевно рекомендував людям скоріше використовувати метод Юсуфа Бактіра, оскільки це простіше.
Даніель Хілл

2

Самостійно написати простий сценарій не важко, запитуючи API PyPI . З останнім Python 3.8 можливо використовувати лише стандартну бібліотеку (при використанні Python 3.7 або новішої версії вам доведеться встановити importlib_metadataрезервний порт):

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

Приклад використання:

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

Якщо у вас випадково packagingвстановлено, це краща альтернатива distutils.versionрозбору версій:

from distutils.version import LooseVersion

...

LooseVersion(s)

стає

from packaging.version import parse

...

parse(s)

Це може дати різні результати піп , якщо додаткові або альтернативні індекси налаштовані для користувача ( з допомогою pip.conf файлу або PIP_INDEX_URL або PIP_EXTRA_INDEX_URL окр вари)
Вім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.