Merge pull request #44552 from Da-Juan/avoid_unneeded_pip_install

pip_state: Check if available upgrades fulfill version requirements.
This commit is contained in:
Nicole Thomas 2017-12-22 14:25:16 -05:00 committed by GitHub
commit 487207f61d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 139 additions and 1 deletions

View file

@ -82,6 +82,7 @@ import re
import shutil
import logging
import sys
from pkg_resources import parse_version
# Import salt libs
import salt.utils
@ -1216,3 +1217,79 @@ def upgrade(bin_env=None,
ret['changes'] = salt.utils.compare_dicts(old, new)
return ret
def list_all_versions(pkg,
bin_env=None,
include_alpha=False,
include_beta=False,
include_rc=False,
user=None,
cwd=None):
'''
.. versionadded:: 2017.7.3
List all available versions of a pip package
pkg
The package to check
bin_env
Path to pip bin or path to virtualenv. If doing a system install,
and want to use a specific pip bin (pip-2.7, pip-2.6, etc..) just
specify the pip bin you want.
include_alpha
Include alpha versions in the list
include_beta
Include beta versions in the list
include_rc
Include release candidates versions in the list
user
The user under which to run pip
cwd
Current working directory to run pip from
CLI Example:
.. code-block:: bash
salt '*' pip.list_all_versions <package name>
'''
pip_bin = _get_pip_bin(bin_env)
cmd = [pip_bin, 'install', '{0}==versions'.format(pkg)]
cmd_kwargs = dict(cwd=cwd, runas=user, output_loglevel='quiet', redirect_stderr=True)
if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
result = __salt__['cmd.run_all'](cmd, **cmd_kwargs)
filtered = []
if not include_alpha:
filtered.append('a')
if not include_beta:
filtered.append('b')
if not include_rc:
filtered.append('rc')
if filtered:
excludes = re.compile(r'^((?!{0}).)*$'.format('|'.join(filtered)))
else:
excludes = re.compile(r'')
versions = []
for line in result['stdout'].splitlines():
match = re.search(r'\s*Could not find a version.* \(from versions: (.*)\)', line)
if match:
versions = [v for v in match.group(1).split(', ') if v and excludes.match(v)]
versions.sort(key=parse_version)
break
if not versions:
return None
return versions

View file

@ -23,6 +23,7 @@ requisite to a pkg.installed state for the package which provides pip
from __future__ import absolute_import
import re
import logging
from pkg_resources import parse_version
# Import salt libs
import salt.utils
@ -94,7 +95,7 @@ def _fulfills_version_spec(version, version_spec):
for oper, spec in version_spec:
if oper is None:
continue
if not salt.utils.compare_versions(ver1=version, oper=oper, ver2=spec):
if not salt.utils.compare_versions(ver1=version, oper=oper, ver2=spec, cmp_func=_pep440_version_cmp):
return False
return True
@ -212,10 +213,70 @@ def _check_if_installed(prefix, state_pkg_name, version_spec,
ret['comment'] = ('Python package {0} was already '
'installed'.format(state_pkg_name))
return ret
if force_reinstall is False and upgrade:
# Check desired version (if any) against currently-installed
include_alpha = False
include_beta = False
include_rc = False
if any(version_spec):
for spec in version_spec:
if 'a' in spec[1]:
include_alpha = True
if 'b' in spec[1]:
include_beta = True
if 'rc' in spec[1]:
include_rc = True
available_versions = __salt__['pip.list_all_versions'](
prefix_realname, bin_env=bin_env, include_alpha=include_alpha,
include_beta=include_beta, include_rc=include_rc, user=user,
cwd=cwd)
desired_version = ''
if any(version_spec):
for version in reversed(available_versions):
if _fulfills_version_spec(version, version_spec):
desired_version = version
break
else:
desired_version = available_versions[-1]
if not desired_version:
ret['result'] = True
ret['comment'] = ('Python package {0} was already '
'installed and\nthe available upgrade '
'doesn\'t fulfills the version '
'requirements'.format(prefix_realname))
return ret
if _pep440_version_cmp(pip_list[prefix_realname], desired_version) == 0:
ret['result'] = True
ret['comment'] = ('Python package {0} was already '
'installed'.format(state_pkg_name))
return ret
return ret
def _pep440_version_cmp(pkg1, pkg2, ignore_epoch=False):
'''
Compares two version strings using pkg_resources.parse_version.
Return -1 if version1 < version2, 0 if version1 ==version2,
and 1 if version1 > version2. Return None if there was a problem
making the comparison.
'''
normalize = lambda x: str(x).split('!', 1)[-1] if ignore_epoch else str(x)
pkg1 = normalize(pkg1)
pkg2 = normalize(pkg2)
try:
if parse_version(pkg1) < parse_version(pkg2):
return -1
if parse_version(pkg1) == parse_version(pkg2):
return 0
if parse_version(pkg1) > parse_version(pkg2):
return 1
except Exception as exc:
logger.exception(exc)
return None
def installed(name,
pkgs=None,
pip_bin=None,