Bagaimana cara memeriksa apakah paket python adalah versi terbaru secara terprogram?

29

Bagaimana Anda memeriksa apakah suatu paket berada pada versi terbaru secara terprogram dalam skrip dan mengembalikan benar atau salah?

Saya dapat memeriksa dengan skrip seperti ini:

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

atau dengan baris perintah:

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

Tetapi bagaimana saya memeriksa secara program dan mengembalikan benar atau salah?

Yusuf
sumber
4
bukan solusi lengkap tetapi mungkin memberi Anda beberapa ide. stackoverflow.com/questions/4888027/…
reyPanda
Bukankah pip memiliki api yang bisa Anda hubungi?
Aluan Haddad
3
Jika Anda dapat memanfaatkannya, Python 3.8 telah meningkatkan dukungan untuk hal-hal ini, setidaknya pada apa yang diinstal secara lokal . docs.python.org/3/library/importlib.metadata.html
JL Peyret
1
piptidak memiliki API. Anda mungkin ingin menonton pip-apiproyek, tetapi belum banyak di sana.
wim

Jawaban:

16

Versi Cepat (Memeriksa paket saja)

Kode di bawah ini memanggil paket dengan versi yang tidak tersedia seperti pip install package_name==random. Panggilan mengembalikan semua versi yang tersedia. Program membaca versi terbaru.

Program kemudian berjalan pip show package_namedan mendapatkan versi paket saat ini.

Jika menemukan kecocokan, itu mengembalikan Benar, jika tidak Salah.

Ini adalah opsi yang andal mengingat berdiri 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

Kode berikut ini untuk 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
Yusuf Baktir
sumber
Saya telah memperbaruinya. Sekarang hanya memeriksa paket pengguna yang tertarik. Saya telah menempatkan kedua alternatif.
Yusuf Baktir
1
Biasanya if (boolean): return True else: return Falselebih baik hanya untukreturn boolean
wim
Tidak baik menggunakan "0" sebagai nomor versi palsu, karena kadang-kadang ini hanya akan melanjutkan dan menginstal paket! (coba pip install p==0misalnya).
wim
Saya telah menggunakan randomsaya yakin tidak ada versi acak
Yusuf Baktir
10

Proyek saya johnnydepmemiliki fitur ini.

Dalam shell:

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

Dengan Python:

>>> from johnnydep.lib import JohnnyDist
>>> dist = JohnnyDist("gekko")
>>> dist.version_installed  
'0.2.0'
>>> dist.version_latest 
'0.2.3'
wim
sumber
Ketika saya menjalankan ini di Prompt Perintah Windows, saya mendapatkan kode pelarian ANSI yang membuat hasilnya tidak dapat dibaca. Saya pikir Anda mungkin ingin memperbaikinya?
user541686
Saya memiliki colorama == 0.4.1 dan structlog == 19.2.0, dan ya, saya juga melihat kode pelarian dengan perintah itu. Jika itu membantu saya menjalankan ini pada Windows 10.0.17763.195, Python 3.7.4. Sayangnya saya tidak memiliki kesempatan untuk memposting masalah.
user541686
@ user541686 Ini masalah232 , diselesaikan di structlog v20.1.0 dirilis hari ini. Terima kasih atas laporannya.
wim
keren terima kasih!
user541686
4

Sunting: Hapus pencarian pip

Terima kasih atas beberapa sarannya. Ini adalah versi baru yang tidak digunakan pip searchtetapi justru menarik versi terbaru langsung dari yang pypidiusulkan oleh Daniel Hill . Ini juga menyelesaikan masalah dengan substring pertandingan yang salah.

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

Respon Asli

Berikut adalah solusi cepat yang mengambil informasi versi terbaru hanya pada gekkopaket yang menarik.

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

Ini menghasilkan pesan Latest version (0.2.3) of gekko is installeddan kembali Trueuntuk menunjukkan versi terbaru (atau Falsejika bukan versi terbaru). Ini mungkin bukan solusi terbaik karena hanya memeriksa substring versi if d[name] in s.decode():tetapi lebih cepat dari pip list --outdateditu memeriksa semua paket. Ini bukan metode yang paling dapat diandalkan karena akan mengembalikan yang salah Truejika versi yang diinstal saat ini 0.2.3tetapi versi terbaru 0.2.30atau 0.2.3a. Suatu perbaikan akan secara terprogram mendapatkan versi terbaru dan melakukan perbandingan langsung.

John Hedengren
sumber
Hati-hati dengan pip search. Itu menggunakan XML-RPC API yang sudah usang, dan kadang-kadang hasil pencarian tidak akurat / salah. Bahkan saya pikir itu mungkin akan segera dihapus, lihat Menghapus perintah pencarian pip # 5216 .
wim
Saya memodifikasi kode untuk menggunakan metode Daniel dalam menarik versi paket saat ini. Cara lain untuk mendapatkan versi gekko saat ini adalah dengan melakukannya import gekkodan current_version=gekko.__version__alih-alih membuat kamus dari semua versi paket. Namun, tidak semua paket memiliki nomor versi yang dapat diakses dalam paket.
John Hedengren
4

Versi terbaru:

Proyek saya ludditememiliki fitur ini:

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

Versi yang diinstal:

Cara kanonik untuk memeriksa versi yang terinstal hanya untuk mengakses __version__atribut namespace tingkat atas:

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

Sayangnya tidak semua proyek mengatur atribut ini. Jika tidak, Anda dapat menggunakannya pkg_resourcesuntuk menggali metadata:

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

Ini harus melakukan trik setidaknya untuk keperluan demo. Cukup panggil isLatestVersiondengan nama paket yang ingin Anda periksa. Jika Anda menggunakan ini di tempat yang penting, Anda ingin mencoba / menangkap permintaan url karena akses internet mungkin tidak tersedia. Perhatikan juga bahwa jika paket tidak diinstal isLatestVersionakan mengembalikan False.

Ini diuji untuk Python 3.7.4 dan 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
Daniel Hill
sumber
1
pip._internalbukan API publik. Itu bahkan secara eksplisit tidak disarankan dalam dokumen pip : " Anda tidak boleh menggunakan API internal pip dengan cara ini ".
wim
@wim Senang tahu. Saya tidak menyadari hal ini. Terima kasih telah memberi tahu saya. Saya pasti akan merekomendasikan orang menggunakan metode Yusuf Baktir sebagai gantinya karena lebih sederhana.
Daniel Hill
2

Tidaklah sulit untuk menulis skrip sederhana sendiri dengan menanyakan API PyPI . Dengan Python 3.8 terbaru, dimungkinkan hanya menggunakan pustaka standar (saat menggunakan Python 3.7 atau lebih lama, Anda harus menginstal importlib_metadatabackport):

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

Contoh penggunaan:

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

Jika Anda telah packagingmenginstal, ini adalah alternatif yang lebih baik distutils.versionuntuk parsing versi:

from distutils.version import LooseVersion

...

LooseVersion(s)

menjadi

from packaging.version import parse

...

parse(s)
hoefling
sumber
Ini dapat memberikan hasil yang berbeda untuk pip jika indeks tambahan atau alternatif dikonfigurasi untuk pengguna (melalui file pip.conf atau PIP_INDEX_URL atau PIP_EXTRA_INDEX_URL env vars)
wim