From fbe648ce8b79d67951097cc422182a684711da96 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Sat, 27 Apr 2019 23:39:48 +0000 Subject: [PATCH] Fix broken pip state --- salt/states/pip_state.py | 72 ++++++++++++++++------------- tests/unit/states/test_pip_state.py | 28 +++++------ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/salt/states/pip_state.py b/salt/states/pip_state.py index ef862207dc9e..64f4ad57fbc7 100644 --- a/salt/states/pip_state.py +++ b/salt/states/pip_state.py @@ -49,42 +49,35 @@ HAS_PIP = True except ImportError: HAS_PIP = False + # Remove references to the loaded pip module above so reloading works + import sys + pip_related_entries = [ + (k, v) for (k, v) in sys.modules.items() + or getattr(v, '__module__', '').startswith('pip.') + or (isinstance(v, types.ModuleType) and v.__name__.startswith('pip.')) + ] + for name, entry in pip_related_entries: + sys.modules.pop(name) + del entry + + del pip + sys_modules_pip = sys.modules.pop('pip', None) + if sys_modules_pip is not None: + del sys_modules_pip if HAS_PIP is True: - try: - from pip.req import InstallRequirement - _from_line = InstallRequirement.from_line - except ImportError: - # pip 10.0.0 move req module under pip._internal - try: - try: - from pip._internal.req import InstallRequirement - _from_line = InstallRequirement.from_line - except AttributeError: - from pip._internal.req.constructors import install_req_from_line as _from_line - except ImportError: - HAS_PIP = False - # Remove references to the loaded pip module above so reloading works - import sys - pip_related_entries = [ - (k, v) for (k, v) in sys.modules.items() - or getattr(v, '__module__', '').startswith('pip.') - or (isinstance(v, types.ModuleType) and v.__name__.startswith('pip.')) - ] - for name, entry in pip_related_entries: - sys.modules.pop(name) - del entry - - del pip - sys_modules_pip = sys.modules.pop('pip', None) - if sys_modules_pip is not None: - del sys_modules_pip - - try: + if salt.utils.versions.compare(ver1=pip.__version__, + oper='>=', + ver2='18.1'): + from pip._internal.exceptions import InstallationError + elif salt.utils.versions.compare(ver1=pip.__version__, + oper='>=', + ver2='10.0'): from pip.exceptions import InstallationError - except ImportError: + else: InstallationError = ValueError + # pylint: enable=import-error log = logging.getLogger(__name__) @@ -93,6 +86,23 @@ __virtualname__ = 'pip' +def _from_line(*args, **kwargs): + import pip + if salt.utils.versions.compare(ver1=pip.__version__, + oper='>=', + ver2='18.1'): + import pip._internal.req.constructors + return pip._internal.req.constructors.install_req_from_line(*args, **kwargs) + elif salt.utils.versions.compare(ver1=pip.__version__, + oper='>=', + ver2='10.0'): + import pip._internal.req + return pip._internal.req.InstallRequirement.from_line(*args, **kwargs) + else: + import pip.req + return pip.req.InstallRequirement.from_line(*args, **kwargs) + + def __virtual__(): ''' Only load if the pip module is available in __salt__ diff --git a/tests/unit/states/test_pip_state.py b/tests/unit/states/test_pip_state.py index 689182d6852d..9dbf06301571 100644 --- a/tests/unit/states/test_pip_state.py +++ b/tests/unit/states/test_pip_state.py @@ -9,6 +9,7 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import logging # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin, SaltReturnAssertsMixin @@ -27,6 +28,9 @@ HAS_PIP = False +log = logging.getLogger(__name__) + + @skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PIP, 'The \'pip\' library is not importable(installed system-wide)') @@ -42,6 +46,7 @@ def setup_loader_modules(self): } def test_install_requirements_parsing(self): + log.debug("Real pip version is %s", pip.__version__) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) pip_list = MagicMock(return_value={'pep8': '1.3.3'}) pip_version = pip.__version__ @@ -50,19 +55,16 @@ def test_install_requirements_parsing(self): with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, 'pip.list': pip_list}): with patch.dict(pip_state.__opts__, {'test': True}): - if salt.utils.versions.compare(ver1=pip_version, - oper='<', - ver2='10.0'): - ret = pip_state.installed('pep8=1.3.2') - self.assertSaltFalseReturn({'test': ret}) - self.assertInSaltComment( - 'Invalid version specification in package pep8=1.3.2. ' - '\'=\' is not supported, use \'==\' instead.', - {'test': ret}) - else: - self.assertRaises( - pip._internal.exceptions.InstallationError, - pip_state.installed, 'pep=1.3.2') + log.debug( + 'pip_state._from_line globals: %s', + [name for name in pip_state._from_line.__globals__] + ) + ret = pip_state.installed('pep8=1.3.2') + self.assertSaltFalseReturn({'test': ret}) + self.assertInSaltComment( + 'Invalid version specification in package pep8=1.3.2. ' + '\'=\' is not supported, use \'==\' instead.', + {'test': ret}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) pip_list = MagicMock(return_value={'pep8': '1.3.3'})