diff --git a/aws_lambda_builders/workflows/python_pip/compat.py b/aws_lambda_builders/workflows/python_pip/compat.py index ff35cbabe..2eb885f20 100644 --- a/aws_lambda_builders/workflows/python_pip/compat.py +++ b/aws_lambda_builders/workflows/python_pip/compat.py @@ -8,15 +8,23 @@ def pip_import_string(python_exe): cmd = [ python_exe, "-c", - "import pip; assert int(pip.__version__.split('.')[0]) <= 9" + "import pip; print(pip.__version__)" ] - p = os_utils.popen(cmd,stdout=os_utils.pipe, stderr=os_utils.pipe) - p.communicate() + p = os_utils.popen(cmd, stdout=os_utils.pipe, stderr=os_utils.pipe) + stdout, stderr = p.communicate() + pip_version = stdout.decode('utf-8').strip() + pip_major_version = int(pip_version.split('.')[0]) + pip_minor_version = int(pip_version.split('.')[1]) + # Pip moved its internals to an _internal module in version 10. # In order to be compatible with version 9 which has it at at the # top level we need to figure out the correct import path here. - if p.returncode == 0: + if pip_major_version == 9: return 'from pip import main' + # Pip changed their import structure again in 19.3 + # https://github.com/pypa/pip/commit/09fd200 + elif pip_major_version >= 19 and pip_minor_version >= 3: + return 'from pip._internal.main import main' else: return 'from pip._internal import main' diff --git a/tests/unit/workflows/python_pip/test_packager.py b/tests/unit/workflows/python_pip/test_packager.py index 937f52aad..452d23987 100644 --- a/tests/unit/workflows/python_pip/test_packager.py +++ b/tests/unit/workflows/python_pip/test_packager.py @@ -1,5 +1,6 @@ import sys from collections import namedtuple +from unittest import TestCase import mock import pytest @@ -70,7 +71,7 @@ def osutils(): class FakePopen(object): def __init__(self, rc, out, err): - self.returncode = 0 + self.returncode = rc self._out = out self._err = err @@ -303,14 +304,24 @@ def test_inject_unknown_error_if_no_stderr(self, pip_factory): assert str(einfo.value) == 'Unknown error' -class TestSubprocessPip(object): +class TestSubprocessPip(TestCase): def test_does_use_custom_pip_import_string(self): fake_osutils = FakePopenOSUtils([FakePopen(0, '', '')]) expected_import_statement = 'foobarbaz' pip = SubprocessPip(osutils=fake_osutils, - import_string=expected_import_statement) + import_string=expected_import_statement, + python_exe=sys.executable) pip.main(['--version']) pip_execution_string = fake_osutils.popens[0][0][0][2] import_statement = pip_execution_string.split(';')[1].strip() assert import_statement == expected_import_statement + + def test_check_pip_runner_string_pip(self): + fake_osutils = FakePopenOSUtils([FakePopen(0, '', '')]) + pip = SubprocessPip(osutils=fake_osutils, + python_exe=sys.executable) + pip.main(['--version']) + + pip_runner_string = fake_osutils.popens[0][0][0][2].split(";")[-1:][0] + self.assertIn("main", pip_runner_string)