Prevent package from being installed on old Python versions
Asked Answered
M

2

19

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

So I expect the following behaviour if pip --version is tied to python 2.7:

$ 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. There are other Python3-only releases, curio for example, which actually install fine - because the setup.py didn't use anything Python 3 specific - only to fail at import time when some Python 3 only syntax is used. And I'm sure there are packages which install OK, import OK, and maybe only fail at runtime!

What is the correct method to specify your supported Python versions in a way that pip will respect? I've found a workaround, involving uploading only a wheel file, and refusing to uploading a .tar.gz distribution, but I would be interested to know the correct fix.


Edit: How does pip know not to download the wheel distribution if the Python/os/architecture is not matching? Does it just use the .whl filename convention or is there something more sophisticated than that happening behind the scenes? Can we somehow give the metadata to a source distribution to make pip do the right thing with .tar.gz uploads?

Merrill answered 15/2, 2017 at 0:0 Comment(7)
I don't know that this is the right answer... but if magicstack is python3 only because setup.py fails on python2.x, then it seems like one possible way to make this work is to force your setup.py to fail on inappropriate python versions...Limon
Hacky. If pip is tied to unsupported Python interpreter, I don't want the package collected or downloaded at all, let alone an installation attempted by executing setup.py.Merrill
Google turns up some previous questions with answers saying to have your setup.py check the Python version.Wein
Relevant: packaging.python.org/guides/…, which mentions that "Although the list of classifiers is often used to declare what Python versions a project supports, this information is only used for searching & browsing projects on PyPI, not for installing projects. To actually restrict what Python versions a project can be installed on, use the python_requires argument."Portamento
Relevant: https://mcmap.net/q/266655/-setup-py-restrict-the-allowable-version-of-the-python-interpreterPortamento
Relevant: https://mcmap.net/q/266655/-setup-py-restrict-the-allowable-version-of-the-python-interpreterPortamento
Relevant: https://mcmap.net/q/265472/-enforcing-python-version-in-setup-pyPortamento
C
18

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 they're for.

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:

setup(
    ...
    python_requires='>=3',
    ...
)

If your package is for Python 3.3 and up but you're not willing to commit to Python 4 support yet, write:

setup(
    ...
    python_requires='~=3.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.*, <4',
    ...
)

And so on.

Once you've done that, you will need to upgrade your version of setuptools to at least 24.2.0 in order for the python_requires argument to be processed; earlier versions will just ignore it with a warning. All of your project's sdists and wheels built afterwards will then contain the relevant metadata that tells PyPI to tell pip what Python versions they're for.

Cavazos answered 14/3, 2017 at 17:13 Comment(5)
@wim: What version specifier did you use for python_requires?Cavazos
I've uploaded a source distribution for testing on pypi (see here for the implementation), and it seems to be working as you've described on pypi. I could only reproduce incorrect behaviour on a different index. It's possible I've come across a bug in devpi server, will investigate more tomorrow...Merrill
See here for a lot more useful information:python3statement.org/practicalitiesCobra
My problem was indeed due to bug in the index (older versions of devpi server are not serving python_requires metadata correctly - I believe it's resolved in current releases though).Merrill
I think it is worth mentioning that the project MUST be installed using pip3 install [-e] (and not "straightforward" using python3 setup.py install) as mentioned here: python3statement.org/practicalitiesValentinevalentino
U
1

The magicstack distribution on pypi is broken. It's failing because the source distribution doesn't contain a magicstack package even though the setup.py for the source distribution says it should.

As long as pypi contains a source distribution (e.g. .tar.gz, .zip), pip will download that if it can't find a matching binary distribution (e.g. .egg, .whl) for your version of python/os/architecture.

One option is to only upload binary distributions to PyPI (preferably wheels). The other option is to check the sys.version in your setup.py for compatible versions and raise an exception otherwise.

Unsuspecting answered 15/2, 2017 at 0:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.