I’m not sure there’s a “right thing to do” here any more than there’s a “right thing to do” when declaring dependencies in
install_requires. I would assume that the documentation string
python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4' is not intended as the correct string to use, but rather as a plausible example of a version string that makes use of many of the operations combined. Note that it uses
!=, so with a little extrapolation, you can assume that
<= are available as well, and you are pretty well equipped to describe the versions your software supports.
I think the reason there’s not a “right” answer is that there are trade-offs to be made, and each project needs to decide what side of the line to fall on:
If you pin an upper limit on your version support, you are describing the current state of the support as of release time. The danger of doing this is that because PyPI metadata is immutable, if there is a release that breaks your software, you cannot simply add the upper limit pin, because
pip will fall back to the most recent version that does not have this pin (which is still broken), and erroneously install it. The only thing you can do remedy the situation is cut a new release that supports the new version.
Of course, even if you could retroactively fix the metadata to add the version pin, all that will do is make it so that
pip fails to find your package and fails to install it, so it’s not like it’s a huge improvement for the end users.
By pinning to the upper limit, you don’t necessarily have the above problem, but you do have the problem that you are encoding in
python_requires a guess about whether a certain version of Python will break your package. As @pf_moore mentioned, there’s good reason to believe that if a Python 4 release occurs, it will not be a breaking change, and it’s worth noting that minor breaking changes occur in most/all point releases as well (per the deprecation policy), so your software may still be subject to the problems you have by not pinning or the problems you have by erroneously putting in a version pin for a version that works just fine for your project.
I tend to come down on the side of “don’t pin”, because it not only inaccurately reflects the state of Python version support as of release time by suggesting that certain versions will not work even though I have no specific information to that effect, and because even in the situation where a new Python version breaks my software, the advantage of having predicted this is minimal.
The one time I think it makes sense to pin your versions is when you know that there’s a breaking change in a given version. Even then it doesn’t provide too much value unless you’ve had that version pinned from the beginning.