可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
How do you check if a package is at its latest version programmatically in a script and return a true or false?
I can check with a script like this:
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'])
or with command line:
(base) C:\User>pip search gekko
gekko (0.2.3) - Machine learning and optimization for dynamic systems
INSTALLED: 0.2.3 (latest)
But how do I check programmatically and return true or false?
回答1:
Fast Version (Checking the package only)
The code below calls the package with an unavailable version like pip install package_name==random
. The call returns all the available versions. The program reads the latest version.
The program then runs pip show package_name
and gets the current version of the package.
If it finds a match, it returns True, otherwise False.
This is a reliable option given that it stands on 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
The following code calls for 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
回答2:
My project johnnydep
has this feature.
In shell:
pip install --upgrade pip johnnydep
pip install gekko==0.2.0
In Python:
>>> from johnnydep.lib import JohnnyDist
>>> dist = JohnnyDist("gekko")
>>> dist.version_installed
'0.2.0'
>>> dist.version_latest
'0.2.3'
回答3:
Edit: Remove pip search
Thanks for the several suggestions. Here is a new version that doesn't use pip search
but instead pulls the latest version directly from pypi
as proposed by Daniel Hill. This also resolves the issue with the substring false matches.
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'))
Original Response
Here is a fast solution that retrieves latest version information on only the gekko
package of interest.
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'))
This produces the message Latest version (0.2.3) of gekko is installed
and returns True
to indicate latest version (or False
if not the latest version). This may not be the best solution because it only checks for a version substring with if d[name] in s.decode():
but it is faster than pip list --outdated
that checks all the packages. This isn't the most reliable method because it will return an incorrect True
if current installed version is 0.2.3
but latest version is 0.2.30
or 0.2.3a
. An improvement would be to programmatically get the latest version and do a direct comparison.
回答4:
Latest version:
My project luddite
has this feature:
>>> import luddite
>>> luddite.get_version_pypi("gekko")
'0.2.3'
Installed version:
The canonical way to check installed version is just to access the __version__
attribute of the top-level namespace:
>>> import gekko
>>> gekko.__version__
'0.2.0'
Unfortunately not all projects set this attribute. When they don't, you can use pkg_resources
to dig it out from the metadata:
>>> import pkg_resources
>>> pkg_resources.get_distribution("gekko").version
'0.2.0'
回答5:
This should do the trick at least for demo purposes. Simply call isLatestVersion
with the name of the package you would like to check. If you are using this somewhere important you would want to try/catch the url request as internet access may not be available. Also note that if the package is not installed isLatestVersion
will return False.
This is tested for Python 3.7.4 and 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
回答6:
It's not hard to write a simple script yourself by querying the PyPI API. With the latest Python 3.8, it's possible using only the standard library (when using Python 3.7 or older, you'll have to install the importlib_metadata
backport):
# 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)
Usage example:
$ python check_version.py setuptools
package: setuptools installed: 41.2.0 latest: 41.6.0
If you're happen to have packaging
installed, it's a better alternative to distutils.version
for version parsing:
from distutils.version import LooseVersion
...
LooseVersion(s)
becomes
from packaging.version import parse
...
parse(s)