From 5e573df69c47064e94e99b847bb6644633399ca6 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Fri, 11 Jan 2019 11:12:44 +0100 Subject: [PATCH 1/2] Add deprecate_python fixture to easily deprecate python versions pip usually emits a warning and tests have to be adapted --- tests/conftest.py | 11 +++++++++- tests/functional/test_completion.py | 6 +++--- tests/functional/test_freeze.py | 13 ++++++++---- tests/functional/test_install.py | 10 +++++---- tests/functional/test_install_check.py | 28 +++++++++++++++++--------- tests/lib/__init__.py | 6 ++++++ 6 files changed, 53 insertions(+), 21 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 45e888393ed..a7bf81c5b6d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -277,7 +277,7 @@ def with_wheel(virtualenv, wheel_install): @pytest.fixture -def script(tmpdir, virtualenv): +def script(tmpdir, virtualenv, deprecated_python): """ Return a PipTestEnvironment which is unique to each test function and will execute all commands inside of the unique virtual environment for this @@ -301,6 +301,9 @@ def script(tmpdir, virtualenv): # PipTestEnvironment needs to capture and assert against temp capture_temp=True, assert_no_temp=True, + + # Deprecated python versions produce an extra deprecation warning + pip_expect_stderr=deprecated_python, ) @@ -341,3 +344,9 @@ def pip(self, *args): @pytest.fixture def in_memory_pip(): return InMemoryPip() + + +@pytest.fixture +def deprecated_python(): + """Used to indicate wheither pip deprecated this python version""" + return False diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index 53380bc37f8..b4e93da402b 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -283,10 +283,10 @@ def test_completion_path_after_option(script, data): @pytest.mark.parametrize('flag', ['--bash', '--zsh', '--fish']) -def test_completion_uses_same_executable_name(script, flag): - expect_stderr = sys.version_info[:2] == (3, 3) +def test_completion_uses_same_executable_name(script, flag, deprecated_python): executable_name = 'pip{}'.format(sys.version_info[0]) + # Deprecated python versions produce an extra deprecation warning result = script.run( - executable_name, 'completion', flag, expect_stderr=expect_stderr + executable_name, 'completion', flag, expect_stderr=deprecated_python, ) assert executable_name in result.stdout diff --git a/tests/functional/test_freeze.py b/tests/functional/test_freeze.py index 101164e4804..6948a15b6e2 100644 --- a/tests/functional/test_freeze.py +++ b/tests/functional/test_freeze.py @@ -138,7 +138,7 @@ def test_freeze_editable_not_vcs(script, tmpdir): @pytest.mark.git -def test_freeze_editable_git_with_no_remote(script, tmpdir): +def test_freeze_editable_git_with_no_remote(script, tmpdir, deprecated_python): """ Test an editable Git install with no remote url. """ @@ -146,7 +146,8 @@ def test_freeze_editable_git_with_no_remote(script, tmpdir): script.pip('install', '-e', pkg_path) result = script.pip('freeze') - assert result.stderr == '' + if not deprecated_python: + assert result.stderr == '' # We need to apply os.path.normcase() to the path since that is what # the freeze code does. @@ -460,7 +461,8 @@ def test_freeze_bazaar_clone(script, tmpdir): """) -def test_freeze_with_requirement_option_file_url_egg_not_installed(script): +def test_freeze_with_requirement_option_file_url_egg_not_installed( + script, deprecated_python): """ Test "freeze -r requirements.txt" with a local file URL whose egg name is not installed. @@ -477,7 +479,10 @@ def test_freeze_with_requirement_option_file_url_egg_not_installed(script): 'Requirement file [requirements.txt] contains {}, but package ' "'Does.Not-Exist' is not installed\n" ).format(url) - assert result.stderr == expected_err + if deprecated_python: + assert expected_err in result.stderr + else: + assert expected_err == result.stderr def test_freeze_with_requirement_option(script): diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 4ee64dd8e07..19794ef55d0 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -37,7 +37,8 @@ def test_pep518_uses_build_env(script, data, common_wheels, command, variant): ) -def test_pep518_build_env_uses_same_pip(script, data, pip_src, common_wheels): +def test_pep518_build_env_uses_same_pip( + script, data, pip_src, common_wheels, deprecated_python): """Ensure the subprocess call to pip for installing the build dependencies is using the same version of pip. """ @@ -47,6 +48,7 @@ def test_pep518_build_env_uses_same_pip(script, data, pip_src, common_wheels): 'python', pip_src / 'src/pip', 'install', '--no-index', '-f', common_wheels, '-f', data.packages, data.src.join("pep518-3.0"), + expect_stderr=deprecated_python, ) @@ -162,8 +164,8 @@ def test_pep518_forkbombs(script, data, common_wheels, command, package): @pytest.mark.network -def test_pip_second_command_line_interface_works(script, pip_src, data, - common_wheels): +def test_pip_second_command_line_interface_works( + script, pip_src, data, common_wheels, deprecated_python): """ Check if ``pip`` commands behaves equally """ @@ -171,7 +173,7 @@ def test_pip_second_command_line_interface_works(script, pip_src, data, script.pip_install_local('-f', common_wheels, pip_src) # On old versions of Python, urllib3/requests will raise a warning about # the lack of an SSLContext. - kwargs = {} + kwargs = {'expect_stderr': deprecated_python} if pyversion_tuple < (2, 7, 9): kwargs['expect_stderr'] = True diff --git a/tests/functional/test_install_check.py b/tests/functional/test_install_check.py index b7d9872892b..01032e08b25 100644 --- a/tests/functional/test_install_check.py +++ b/tests/functional/test_install_check.py @@ -1,11 +1,14 @@ from tests.lib import create_test_package_with_setup -def matches_expected_lines(string, expected_lines): - return set(string.splitlines()) == set(expected_lines) +def matches_expected_lines(string, expected_lines, exact=True): + if exact: + return set(string.splitlines()) == set(expected_lines) + # If not exact, check that all expected lines are present + return set(expected_lines) <= set(string.splitlines()) -def test_check_install_canonicalization(script): +def test_check_install_canonicalization(script, deprecated_python): pkga_path = create_test_package_with_setup( script, name='pkgA', @@ -33,7 +36,9 @@ def test_check_install_canonicalization(script): expected_lines = [ "pkga 1.0 requires SPECIAL.missing, which is not installed.", ] - assert matches_expected_lines(result.stderr, expected_lines) + # Deprecated python versions produce an extra warning on stderr + assert matches_expected_lines( + result.stderr, expected_lines, exact=not deprecated_python) assert result.returncode == 0 # Install the second missing package and expect that there is no warning @@ -42,7 +47,8 @@ def test_check_install_canonicalization(script): result = script.pip( 'install', '--no-index', special_path, '--quiet', ) - assert matches_expected_lines(result.stderr, []) + assert matches_expected_lines( + result.stderr, [], exact=not deprecated_python) assert result.returncode == 0 # Double check that all errors are resolved in the end @@ -54,7 +60,8 @@ def test_check_install_canonicalization(script): assert result.returncode == 0 -def test_check_install_does_not_warn_for_out_of_graph_issues(script): +def test_check_install_does_not_warn_for_out_of_graph_issues( + script, deprecated_python): pkg_broken_path = create_test_package_with_setup( script, name='broken', @@ -74,7 +81,9 @@ def test_check_install_does_not_warn_for_out_of_graph_issues(script): # Install a package without it's dependencies result = script.pip('install', '--no-index', pkg_broken_path, '--no-deps') - assert matches_expected_lines(result.stderr, []) + # Deprecated python versions produce an extra warning on stderr + assert matches_expected_lines( + result.stderr, [], exact=not deprecated_python) # Install conflict package result = script.pip( @@ -86,14 +95,15 @@ def test_check_install_does_not_warn_for_out_of_graph_issues(script): "broken 1.0 has requirement conflict<1.0, but " "you'll have conflict 1.0 which is incompatible." ), - ]) + ], exact=not deprecated_python) # Install unrelated package result = script.pip( 'install', '--no-index', pkg_unrelated_path, '--quiet', ) # should not warn about broken's deps when installing unrelated package - assert matches_expected_lines(result.stderr, []) + assert matches_expected_lines( + result.stderr, [], exact=not deprecated_python) result = script.pip('check', expect_error=True) expected_lines = [ diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 935c8f2cf2b..5269b8409ed 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -323,6 +323,10 @@ def __init__(self, base_path, *args, **kwargs): environ["PYTHONIOENCODING"] = "UTF-8" kwargs["environ"] = environ + # Whether all pip invocations should expect stderr + # (useful for Python version deprecation) + self.pip_expect_stderr = kwargs.pop('pip_expect_stderr', None) + # Call the TestFileEnvironment __init__ super(PipTestEnvironment, self).__init__(base_path, *args, **kwargs) @@ -375,6 +379,8 @@ def run(self, *args, **kw): ) def pip(self, *args, **kwargs): + if self.pip_expect_stderr: + kwargs['expect_stderr'] = True # On old versions of Python, urllib3/requests will raise a warning # about the lack of an SSLContext. Expect it when running commands # that will touch the outside world. From b268c67f84b1e4ef93c67831c8250d3d5bb1d3c4 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Fri, 11 Jan 2019 11:14:25 +0100 Subject: [PATCH 2/2] Deprecate Python 3.4 --- news/6106.removal | 1 + src/pip/_internal/cli/base_command.py | 10 ++++++++++ tests/conftest.py | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 news/6106.removal diff --git a/news/6106.removal b/news/6106.removal new file mode 100644 index 00000000000..56bd9a9c7ff --- /dev/null +++ b/news/6106.removal @@ -0,0 +1 @@ +Deprecate support for Python 3.4 diff --git a/src/pip/_internal/cli/base_command.py b/src/pip/_internal/cli/base_command.py index a8371aa8754..e1f45826ce4 100644 --- a/src/pip/_internal/cli/base_command.py +++ b/src/pip/_internal/cli/base_command.py @@ -26,6 +26,7 @@ install_req_from_editable, install_req_from_line, ) from pip._internal.req.req_file import parse_requirements +from pip._internal.utils.deprecation import deprecated from pip._internal.utils.logging import setup_logging from pip._internal.utils.misc import ( get_prog, normalize_path, redact_password_from_url, @@ -134,6 +135,15 @@ def main(self, args): user_log_file=options.log, ) + if sys.version_info[:2] == (3, 4): + deprecated( + "Python 3.4 support has been deprecated. pip 19.1 will be the " + "last one supporting it. Please upgrade your Python as Python " + "3.4 won't be maintained after March 2019 (cf PEP 429).", + replacement=None, + gone_in='19.2', + ) + # TODO: Try to get these passing down from the command? # without resorting to os.environ to hold these. # This also affects isolated builds and it should. diff --git a/tests/conftest.py b/tests/conftest.py index a7bf81c5b6d..1b51fc8d322 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -349,4 +349,4 @@ def in_memory_pip(): @pytest.fixture def deprecated_python(): """Used to indicate wheither pip deprecated this python version""" - return False + return sys.version_info[:2] == (3, 4)