Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate Python 3.4 #6123

Merged
merged 2 commits into from
Jan 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/6106.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate support for Python 3.4
10 changes: 10 additions & 0 deletions src/pip/_internal/cli/base_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 10 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
)


Expand Down Expand Up @@ -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 sys.version_info[:2] == (3, 4)
6 changes: 3 additions & 3 deletions tests/functional/test_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
13 changes: 9 additions & 4 deletions tests/functional/test_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,16 @@ 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.
"""
pkg_path = _create_test_package(script)
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.
Expand Down Expand Up @@ -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.
Expand All @@ -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):
Expand Down
10 changes: 6 additions & 4 deletions tests/functional/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand All @@ -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,
)


Expand Down Expand Up @@ -162,16 +164,16 @@ 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<PYVERSION>`` commands behaves equally
"""
# Re-install pip so we get the launchers.
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

Expand Down
28 changes: 19 additions & 9 deletions tests/functional/test_install_check.py
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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',
Expand All @@ -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(
Expand All @@ -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 = [
Expand Down
6 changes: 6 additions & 0 deletions tests/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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.
Expand Down