Repository: pypa/pip

Issue:
Deprecate/Drop Support for Python 3.3?
Currently Python 3.3 support is not a major headache for support (unlike 3.2) because we're largely being limited by Python 2.6 and 2.7 in terms of what features we can support. However, I think it's important to periodically look at the usage and make sure that we're not supporting Python versions that are not really being used as no matter what, each version of Python we support incurs a cost in terms of overhead for support (more build matrix items, minor incompatibilities, etc).

With that in mind, I took a look at what % of the pip initiated traffic that PyPI received in the last month to see what our usage numbers look like.

Only pip 8 initiated traffic:

```
2.7 83.8%
3.5  6.3%
3.4  5.5%
2.6  3.8%
3.3  0.3%
3.6  0.1%
3.2  0.03%
```

All pip initiated traffic:

```
2.7 86.8%
3.4  5.2%
3.5  3.9%
2.6  3.4%
3.3  0.4%
3.2  0.07%
3.6  0.04%
```

Given that 3.3 support is well under 1%, do we want to deprecate support for Python 3.3 with intent to drop support for it in either pip 9 or pip 10? For myself, I say yes-- either as a pip 9 or a pip 10 deprecation.

@pypa/pip-committers ?



Discussion:
Since it is not particularly painful to keep 3.3, I'd say we should keep supporting it until its end of life i.e. [September 2017](https://www.python.org/dev/peps/pep-0398/#lifespan) which could correspond approximately to pip 10 release since we usually have a major pip version every 6 months or so.

So maybe make a special deprecation like we have for Python 2.6 and just remove it in the next major version we release after September 2017?

i propose keeping it up until pypy manages to catch up

> So maybe make a special deprecation like we have for Python 2.6 and just remove it in the next major version we release after September 2017?

It seems fine to me.

On the other end, Python 3.3 is not supported any more for bugfix, only for security updates so dropping its support in the next major could also be done.

Whatever we choose, we should try to deal with it more gracefully:
- Do not prompt for upgrade message in 3.3 if the next version is the major known to drop support for 3.3
- Implement `Python-Requires` in the mean time to prevent 3.3 user from upgrading to an unsupported version of pip

For Python 3.x releases I don't think we should support anything that is no longer supported by python-dev (see https://docs.python.org/devguide/#status-of-python-branches). That means 3.3+ at the moment. I don't have a problem with being a little more aggressive than that if there's a benefit for us in doing so, but I'm not sure dropping 3.3 gives us anything much.

I'll leave the question of 2.x support for others (my views are basically as above, follow python-dev, but that's more aggressive than current policy :-))

As regards the point @RonnyPfannschmidt made about PyPy, I'm not sure PyPy3 is worth worrying about yet (can we get PyPy vs CPython download figures?) - they don't seem to have any published release schedule or plans for tracking CPython 3 versions, so we'd in effect be pinning support for an old CPython version indefinitely (or at least until we decide to ignore PyPy3 at some later date). I'd rather we took the view that we support PyPy to the extent that they are compatible with one of the Python versions we support.

But I do agree with @xavfernandez that we need to improve our desupport process, so that if someone is on a version that's no longer supported, they can't accidentally upgrade to a pip version that they can't use, and they don't get nagged to do so.

I think I'm going to go ahead and drop support for 3.3 for the next major version of pip (10.0). We're only a couple of months away from 3.3 being EOL with upstream and they haven't released a new version since 2014-10-12 anyways so while it is supported, I would argue that it is more like "supported". In addition PyPy3 is soon to have a 3.5 compatible release, so that is no longer a reason to keep this around.

Ultimately though, I think the usage just doesn't warrant spending much effort in keeping it here.
> I think I'm going to go ahead and drop support for 3.3 for the next major version of pip (10.0).

Do you mean that pip 9.0.1 will be the last to support 3.3 or will pip 10 be the last ?

I'm much more in favor of the second solution.
We'd add DepreciationWarning in pip 10 saying it will be the last supporting 3.3 to give the (very) few people still using 3.3 a heads up.

What is to gain to drop 3.3 right away ? (Except one less Travis build)
Sure that works for me. The main thing to gain is one less Travis build :)

Resolving patch:
diff --git a/pip/basecommand.py b/pip/basecommand.py
--- a/pip/basecommand.py
+++ b/pip/basecommand.py
@@ -6,6 +6,7 @@
 import os
 import sys
 import optparse
+import warnings
 
 from pip import cmdoptions
 from pip.index import PackageFinder
@@ -20,7 +21,7 @@
     SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND,
     PREVIOUS_BUILD_DIR_ERROR,
 )
-from pip.utils import get_prog, normalize_path
+from pip.utils import deprecation, get_prog, normalize_path
 from pip.utils.logging import IndentingFormatter
 from pip.utils.outdated import pip_version_check
 
@@ -185,6 +186,13 @@ def main(self, args):
             ),
         })
 
+        if sys.version_info[:2] == (3, 3):
+            warnings.warn(
+                "Python 3.3 supported has been deprecated and support for it "
+                "will be dropped in the future. Please upgrade your Python.",
+                deprecation.RemovedInPip11Warning,
+            )
+
         # TODO: try to get these passing down from the command?
         #      without resorting to os.environ to hold these.
 


Test patch:
diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py
--- a/tests/functional/test_install.py
+++ b/tests/functional/test_install.py
@@ -70,7 +70,7 @@ def test_pip_second_command_line_interface_works(script, data):
     # On old versions of Python, urllib3/requests will raise a warning about
     # the lack of an SSLContext.
     kwargs = {}
-    if pyversion_tuple < (2, 7, 9):
+    if pyversion_tuple < (2, 7, 9) or pyversion_tuple[:2] == (3, 3):
         kwargs['expect_stderr'] = True
 
     args = ['pip%s' % pyversion]
diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py
--- a/tests/functional/test_install_upgrade.py
+++ b/tests/functional/test_install_upgrade.py
@@ -1,4 +1,3 @@
-import json
 import os
 import sys
 import textwrap
@@ -449,27 +448,3 @@ def prep_ve(self, script, version, pip_src, distribute=False):
             cwd=pip_src,
             expect_stderr=True,
         )
-
-    @pytest.mark.skipif(
-        sys.version_info >= (3, 5),
-        reason="distribute doesn't work on Python 3.5",
-    )
-    def test_from_distribute_6_to_setuptools_7(
-            self, script, data, virtualenv):
-        self.prep_ve(
-            script, '1.9.1', virtualenv.pip_source_dir, distribute=True
-        )
-        result = self.script.run(
-            self.ve_bin / 'pip', 'install', '--no-index',
-            '--find-links=%s' % data.find_links, '-U', 'distribute',
-        )
-        assert (
-            "Found existing installation: distribute 0.6.34" in result.stdout
-        )
-        result = self.script.run(
-            self.ve_bin / 'pip', 'list', '--format=json',
-        )
-        assert {"name": "setuptools", "version": "0.9.8"} \
-            in json.loads(result.stdout)
-        assert {"name": "distribute", "version": "0.7.3"} \
-            in json.loads(result.stdout)
diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py
--- a/tests/functional/test_uninstall.py
+++ b/tests/functional/test_uninstall.py
@@ -54,11 +54,11 @@ def test_simple_uninstall_distutils(script):
         in json.loads(result.stdout)
     result = script.pip('uninstall', 'distutils_install', '-y',
                         expect_stderr=True, expect_error=True)
-    assert result.stderr.strip() == (
+    assert (
         "Cannot uninstall 'distutils-install'. It is a distutils installed "
         "project and thus we cannot accurately determine which files belong "
         "to it which would lead to only a partial uninstall."
-    )
+    ) in result.stderr
 
 
 @pytest.mark.network
diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py
--- a/tests/lib/__init__.py
+++ b/tests/lib/__init__.py
@@ -334,6 +334,9 @@ def pip(self, *args, **kwargs):
         if (pyversion_tuple < (2, 7, 9) and
                 args and args[0] in ('search', 'install', 'download')):
             kwargs['expect_stderr'] = True
+        # Python 3.3 is deprecated and we emit a warning on it.
+        if pyversion_tuple[:2] == (3, 3):
+            kwargs['expect_stderr'] = True
 
         return self.run("pip", *args, **kwargs)