pythonpipsetuptoolspypipython-packaging

Prevent package from being installed on old Python versions


What can we put in a setup.py file to prevent pip from collecting and attempting to install a package when using an unsupported Python version?

For example magicstack is a project listed with the trove classifier:

Programming Language :: Python :: 3 :: Only

Therefore, the expected result is expected when the pip is run in Python 2.x is something like:

$ pip install magicstack
Collecting magicstack
  Could not find a version that satisfies the requirement magicstack (from versions: )
No matching distribution found for magicstack

But the actual behavior is that pip collects a release, downloads it, attempts to install it, and fails.

A worse example: curio is Python 3 only, but actually installs fine on 2.7 (because the setup.py didn't use any py3 specific features) only to fail at import time because some py3 only syntax is used in the code.

And I'm sure there are packages out there which install OK, import OK, and only fail at runtime because of py2/py3 incompatibilities!

What is the correct method to specify your supported Python versions in a way that pip will respect? The solution must work for both wheels and source distributions.


Solution

  • There is a correct way to do this, but unfortunately pip only started supporting it in version 9.0.0 (released 2016-11-02), and so users with older versions of pip will continue to download packages willy-nilly regardless of what Python version the packages actually support.

    If you need to prevent installation on client pip versions older than 9.0.0, or prevent installation when python setup.py install is used instead of pip, then the only way is to check sys.version from within the setup.py and raise an exception for unsupported Python versions. Otherwise, read on for the modern solution.

    In your setup.py file, pass setup() a python_requires argument that lists your package's supported Python versions as a PEP 440 version specifier. For example, if your package is for Python 3+ only, write in your setup.py file:

    from setuptools import setup
    
    setup(
        ...
        python_requires=">=3",
        ...
    )
    

    If your package is for Python 2.6, 2.7, and all versions of Python 3 starting with 3.3, write:

    setup(
        ...
        python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*',
        ...
    )
    

    And so on.

    In a pyproject.toml file, the equivalent would be:

    [project]
    ...
    requires-python = ">=3"
    

    When building your package, you will need to upgrade your version of setuptools to at least 24.2.0 in order for the Requires-Python metadata field to be correctly set; earlier versions of setuptools will just ignore it with a warning. Your project's sdists and wheels built will then contain the relevant metadata that tells PyPI to tell pip what Python versions they support.