Search code examples
pythonpython-3.xpython-3.6setuptoolsf-string

When is it acceptable to use features from a later minor version update of python in a setup.py script?


Original question title:

Should we use python 3.6 features in setup.py?

I've created a setup.py for a private library installer which depends on python 3.6. I attempted to make the file a little more friendly for anyone trying to install it on python versions prior and realized I had a major flaw in my thinking.

I'm using f-strings in the setup.py which only work in >=3.6!

Anyone trying to run the setup.py in a previous version of python won't even get to the first line of code which checks the interpreter version. So this got me thinking about guidelines for creating a setup.py file and what other syntactical limitations might exist which would prevent setup.py from running properly in multiple environments.

#!/usr/bin/env python3

"""
PIP Install script for XXXXXX packages
"""
import sys
if sys.version_info < (3,6):
    sys.exit('Sorry, Python < 3.6 is not supported')

from setuptools import setup

NAME = 'abcutil'
AUTHOR = 'XXXXXXXXXX'

CLASSIFIERS = """\
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
Programming Language :: Python
Programming Language :: Python :: 3
Topic :: Software Development
Operating System :: Microsoft :: Windows
Operating System :: Unix
Operating System :: MacOS
"""

MAJOR               = 1
MINOR               = 1
MICRO               = 0
ISRELEASED          = False
VERSION             = f'{MAJOR}.{MINOR}.{MICRO}'

setup(name = NAME,
      maintainer = AUTHOR,
      version=VERSION,
      maintainer_email = 'XXXXXXX',
      description='XXXXXXX utility packages',
      author = AUTHOR,
      download_url = 'https://gitlabXXXXXXXX/XXXXXXX',
      license = 'BSD',
      classifiers=[_f for _f in CLASSIFIERS.split('\n') if _f],
      python_requires=">=3.6",
      packages=[NAME, f"{NAME}.selenium", f"{NAME}.regression_helpers"],
      install_requires = ['selenium==3.9', 'requests>=2.18.4', 'python-dateutil', 'pytz'],
      dependency_links=['']
      )

Running the above will give you this:

$ python setup.py
  File "setup.py", line 30
    VERSION             = f'{MAJOR}.{MINOR}.{MICRO}'
                                                   ^
SyntaxError: invalid syntax

My question is

What is recommended for setup.py when it comes using interpreter features limited to newer versions?


Edit:

3.8.2 is out now and it got me thinking about this question again. So I've re-worded the question.

When is it acceptable to use features from a later minor version update of python in a setup.py script?

F-strings were added in 3.6. At what point does it become acceptable to start using them in the setup.py? Two minor versions? Three? The next major version?


Solution

  • First, let's wrap up an important discussion covered in the comments: your setup.py containing an f-string won't be invoked anyway, unless the client's pip version is extremely old. Older than introduction of support for python_requires metadata, which was in pip 9.0.0 (2016) (changelog). Note that all direct invocations of setup.py are effectively deprecated, and if pip was able to find a wheel distribution then there will be no setup.py executed at all, that file is not even included in the wheel.

    Now to answer the actual question: it's acceptable to use newer language features anytime, even immediately, as long as your package metadata communicates the minimum required Python version which is supported.

    You're already doing this in the example code in the question by providing a lower bound on the python_requires:

    setup(
        ...
        python_requires=">=3.6",
        ...
    )
    

    The built distribution will have metadata associated advertising that a Python version of at least 3.6 is required, which means Python runtimes older than 3.6 will not attempt to install the package in the first place. In fact, pip will not even attempt to download it in that case.

    The Python version requirement will be displayed on the project landing page like this:

    python requires render

    And the actual release file, abcutil-1.1.0.tar.gz, will be "invisible" to lower Python versions for all practical purposes, for example:

    $ python2 -m pip install --index-url=https://test.pypi.org/simple abcutil
    Looking in indexes: https://test.pypi.org/simple
    ERROR: Could not find a version that satisfies the requirement abcutil (from versions: none)
    ERROR: No matching distribution found for abcutil
    

    You may enable pip's verbose logging to witness the package finder seeing and intentionally skipping this distribution:

    $ python2 -m pip install -i https://test.pypi.org/simple abcutil -v |& grep abcutil-1
      Link requires a different Python (2.7.18 not in: u'>=3.6'): https://test-files.pythonhosted.org/packages/89/8a/45a45b35deae088b184b776fb11392e13023ee566d8ff9484dba49a310cc/abcutil-1.1.0.tar.gz#sha256=72aeec98ff91fadaf49eea1f0c3fd9fa45dfb304bbbf4e0b79a04fcbcd76072f (from https://test.pypi.org/simple/abcutil/) (requires-python:>=3.6)