How to write Python code that is able to properly require a minimal python version?

Question:

I would like to see if there is any way of requiring a minimal python version.

I have several python modules that are requiring Python 2.6 due to the new exception handling (as keyword).

It looks that even if I check the python version at the beginning of my script, the code will not run because the interpreter will fail inside the module, throwing an ugly system error instead of telling the user to use a newer python.

Asked By: sorin

||

Answers:

I’m guessing you have something like:

import module_foo
...
import sys
# check sys.version

but module_foo requires a particular version as well? This being the case, it is perfectly valid to rearrange your code thus:

import sys
# check sys.version
import module_foo

Python does not require that imports, aside from from __future__ import [something] be at the top of your code.

Answered By: lvc

You should not use any Python 2.6 features inside the script itself. Also, you must do your version check before importing any of the modules requiring a new Python version.

E.g. start your script like so:

#!/usr/bin/env python
import sys

if sys.version_info[0] != 2 or sys.version_info[1] < 6:
    print("This script requires Python version 2.6")
    sys.exit(1)

# rest of script, including real initial imports, here
Answered By: Walter Mundt
import sys
if sys.hexversion < 0x02060000:
    sys.exit("Python 2.6 or newer is required to run this program.")

import module_requiring_26

Also the cool part about this is that it can be included inside the __init__ file or the module.

Answered By: sorin

Rather than indexing you could always do this,

import platform
if platform.python_version() not in ('2.6.6'):
    raise RuntimeError('Not right version')
Answered By: Jakob Bowyer

To complement the existing, helpful answers:

You may want to write scripts that run with both Python 2.x and 3.x, and require a minimum version for each.

For instance, if your code uses the argparse module, you need at least 2.7 (with a 2.x Python) or at least 3.2 (with a 3.x Python).

The following snippet implements such a check; the only thing that needs adapting to a different, but analogous scenario are the MIN_VERSION_PY2=... and MIN_VERSION_PY3=... assignments.

As has been noted: this should be placed at the top of the script, before any other import statements.

import sys

MIN_VERSION_PY2 = (2, 7)    # min. 2.x version as major, minor[, micro] tuple
MIN_VERSION_PY3 = (3, 2)    # min. 3.x version

# This is generic code that uses the tuples defined above.
if (sys.version_info[0] == 2 and sys.version_info < MIN_VERSION_PY2
      or
    sys.version_info[0] == 3 and sys.version_info < MIN_VERSION_PY3):
      sys.exit(
        "ERROR: This script requires Python 2.x >= %s or Python 3.x >= %s;"
        " you're running %s." % (
          '.'.join(map(str, MIN_VERSION_PY2)), 
          '.'.join(map(str, MIN_VERSION_PY3)), 
          '.'.join(map(str, sys.version_info))
        )
      )

If the version requirements aren’t met, something like the following message is printed to stderr and the script exits with exit code 1.

This script requires Python 2.x >= 2.7 or Python 3.x >= 3.2; you're running 2.6.2.final.0.

Note: This is a substantially rewritten version of an earlier, needlessly complicated answer, after realizing – thanks to Arkady’s helpful answer – that comparison operators such as > can directly be applied to tuples.

Answered By: mklement0

You can take advantage of the fact that Python will do the right thing when comparing tuples:

#!/usr/bin/python
import sys
MIN_PYTHON = (2, 6)
if sys.version_info < MIN_PYTHON:
    sys.exit("Python %s.%s or later is required.n" % MIN_PYTHON)
Answered By: Arkady

I need to make sure I’m using Python 3.5 (or, eventually, higher). I monkeyed around on my own and then I thought to ask SO – but I’ve not been impressed with the answers (sorry, y’all ::smile::). Rather than giving up, I came up with the approach below. I’ve tested various combinations of the min_python and max_python specification tuples and it seems to work nicely:

  • Putting this code into a __init__.py is attractive:

    • Avoids polluting many modules with a redundant version check
    • Placing this at the top of a package hierarchy even more further supports the DRY principal, assuming the entire hierarchy abides by the same Python version contraints
    • Takes advantage of a place (file) where I can use the most portable Python code (e.g. Python 1 ???) for the check logic and still write my real modules in the code version I want
    • If I have other package-init stuff that is not “All Python Versions Ever” compatible, I can shovel it into another module, e.g. __init_p3__.py as shown in the sample’s commented-out final line. Don’t forget to replace the pkgname place holder with the appropriate package name.
  • If you don’t want a min (or max), just set it to = ()

  • If you only care about the major version, just use a “one-ple”, e.g. = (3, ) Don’t forget the comma, otherwise (3) is just a parenthesized (trivial) expression evaluating to a single int
  • You can specify finer min/max than just one or two version levels, e.g. = (3, 4, 1)
  • There will be only one “Consider running as” suggestion when the max isn’t actually greater than the min, either because max is an empty tuple (a “none-ple”?), or has fewer elements.

NOTE: I’m not much of a Windoze programmer, so the text_cmd_min and text_cmd_max values are oriented for *Nix systems. If you fix up the code to work in other environments (e.g. Windoze or some particular *Nix variant), then please post. (Ideally, a single super-smartly code block will suffice for all environments, but I’m happy with my *Nix only solution for now.)

PS: I’m somewhat new to Python, and I don’t have an interpreter with version less than 2.7.9.final.0, so it’s tuff to test my code for earlier variants. On the other hand, does anyone really care? (That’s an actual question I have: In what (real-ish) context would I need to deal with the “Graceful Wrong-Python-Version” problem for interpreters prior to 2.7.9?)

__init__.py

'''Verify the Python Interpreter version is in range required by this package'''

min_python = (3, 5)
max_python = (3, )

import sys

if (sys.version_info[:len(min_python)] < min_python) or (sys.version_info[:len(max_python)] > max_python):

    text_have = '.'.join("%s" % n for n in sys.version_info)
    text_min  = '.'.join("%d" % n for n in min_python) if min_python else None
    text_max  = '.'.join("%d" % n for n in max_python) if max_python else None

    text_cmd_min = 'python' + text_min + '  ' + "  ".join("'%s'" % a for a in sys.argv) if min_python              else None
    text_cmd_max = 'python' + text_max + '  ' + "  ".join("'%s'" % a for a in sys.argv) if max_python > min_python else None

    sys.stderr.write("Using Python version: " + text_have + "n")

    if min_python:  sys.stderr.write(" - Min required: " + text_min + "n")
    if max_python:  sys.stderr.write(" - Max allowed : " + text_max + "n")
    sys.stderr.write("n")

    sys.stderr.write("Consider running as:nn")
    if text_cmd_min:  sys.stderr.write(text_cmd_min + "n")
    if text_cmd_max:  sys.stderr.write(text_cmd_max + "n")
    sys.stderr.write("n")

    sys.exit(9)

# import pkgname.__init_p3__
Answered By: Stevel

Starting with version 9.0.0 pip supports Requires-Python field in distribution’s metadata which can be written by setuptools starting with version 24-2-0. This feature is available through python_requires keyword argument to setup function.

Example (in setup.py):

setup(
...
   python_requires='>=2.5,<2.7',
...

)

To take advantage of this feature one has to package the project/script first if not already done. This is very easy in typical case and should be done nonetheless as it allows users to easily install, use and uninstall given project/script. Please see Python Packaging User Guide for details.

Answered By: Piotr Dobrogost

I used to have a more complicated approach for supporting both Python2 and Python3, but I no longer try to support Python2, so now I just use:

import sys
MIN_PYTHON = (3, 7)
assert sys.version_info >= MIN_PYTHON, f"requires Python {'.'.join([str(n) for n in MIN_PYTHON])} or newer"

If the version check fails, you get a traceback with something like:

AssertionError: requires Python 3.7 or newer

at the bottom.

Answered By: Eric Smith
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.