pythonpython-packaging

How to publish an update to pip *just* for older Python versions?


I have a library published on pip which previously had a minimum Python version of 3.7, and now has a minimum Python version of 3.9.

This means that, when a user with Python 3.7 or 3.8 does pip install my-package, they silently get the last version that was published with 3.7 support, rather than the most recent version. This means they're missing updates that I've made since then; in particular, I just changed the format of an external file that my library fetches as part of its operation, and the old version just breaks on it.

Is there any way to publish a new version of the library just for Python 3.7, so I can print a deprecation message and quit, rather than having it fail with mysterious errors? Would it work, for example, to just go back to the last 3.7 commit, add the deprecation message and kill switch, change my setuptools to advertise 3.7 support, and publish that on pip as a new version, then immediately revert back to my existing code (advertising 3.9) and publish that on top?


Solution

  • Note: pip is an installer, not a package index. In this answer I'm guessing that "published on pip" means "published to a repository which pip can install from", such as PyPI.

    Suppose the last version of your package which had a minimum supported Python version of 3.7 was packaged like this:

    In setup.py:

    from setuptools import setup
    
    setup(
        name="mypkg",
        version="1.2.3",
        python_requires=">=3.7",
    )
    

    Or in pyproject.toml:

    [project]
    name = "mypkg"
    version = "1.2.3"
    requires-python = ">=3.7"
    

    And the first version which had a minimum supported Python version of 3.9 was packaged like this:

    In setup.py:

    from setuptools import setup
    
    setup(
        name="mypkg",
        version="2.0.0",
        python_requires=">=3.9",
    )
    

    Or in pyproject.toml:

    [project]
    name = "mypkg"
    version = "2.0.0"
    requires-python = ">=3.9"
    

    What you can do is go back to the source code of v1.2.3 and make the necessary deprecation / error messages, then publish another release with a version string v which satisfies this inequality

    >>> from packaging.version import Version
    >>> Version("1.2.3") < Version(v) < Version("2.0.0")
    True
    

    For example, v="1.3" would do the trick here. So, checkout the revision from which v1.2.3 was generated (for git users this would probably mean git checkout v1.2.3 and then start a new branch), then make the metadata changes to the version number but leave the Python version requirement as is:

    from setuptools import setup
    
    setup(
        name="mypkg",
        version="1.3",
        python_requires=">=3.7",
    )
    

    Or in pyproject.toml:

    [project]
    name = "mypkg"
    version = "1.3"
    requires-python = ">=3.7"
    

    If you don't know the revision from which 1.2.3 was produced, you could also just download the existing release and edit it manually, but it would be better to use a VCS for this part so that you can push up a tag for the new release as well.

    It is crucial that this new version is still compatible with Python 3.7, so leave the Requires-Python field as ">=3.7" here. It is not necessary for the new release to explicitly exclude 3.9+, like the question suggests. However, since the question asks about making a release "just for Python 3.7", I'll add that this is also possible by using Requires-Python value of ">=3.7,<3.8" where the comma signifies a logical AND.

    Personally, I recommend not to add any upper bound on the Python version, because solvers on 3.9+ will automatically choose the existing "more recent" (by version number) packages anyway, so adding an upper bound just complicates matters unnecessarily.