How to write Python code that is able to properly require a minimal python version?
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开发者_JAVA技巧 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.
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)
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
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.
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.
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.
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.
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.
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 thepkgname
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 singleint
- 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:\n\n")
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__
Rather than indexing you could always do this,
import platform
if platform.python_version() not in ('2.6.6'):
raise RuntimeError('Not right version')
精彩评论