如何在使用新语言功能的程序中检查Python版本?

How can I check for Python version in a program that uses new language features?

如果我有一个python脚本至少需要一个特定的python版本,正确的失败方式是什么?何时使用早期版本的python启动脚本?

如何尽早获得控制权以发出错误消息然后退出?

例如,我有一个程序使用ternery运算符(2.5中新增的)和"with"块(2.6新)。我写了一个简单的小翻译版本检查程序,这是脚本将要做的第一件事打电话给…只是没那么远。相反,脚本在python编译期间失败,在我的例程之前甚至被召唤。因此,脚本的用户看到模糊的synax错误回溯-这几乎需要专家推断这只是一个简单的跑步案例python的错误版本。

我知道如何检查Python的版本。问题是,在旧版本的Python中,某些语法是非法的。考虑这个程序:

1
2
3
4
5
6
7
import sys
if sys.version_info < (2, 4):
    raise"must use python 2.5 or greater"
else:
    # syntax error in 2.4, ok in 2.5
    x = 1 if True else 2
    print x

当在2.4下运行时,我想要这个结果

1
2
$ ~/bin/python2.4 tern.py
must use python 2.5 or greater

而不是这个结果:

1
2
3
4
5
$ ~/bin/python2.4 tern.py
  File"tern.py", line 5
    x = 1 if True else 2
           ^
SyntaxError: invalid syntax

(为同事通灵。)


您可以使用eval进行测试:

1
2
3
4
try:
  eval("1 if True else 2")
except SyntaxError:
  # doesn't have ternary

另外,在python2.5中可以使用with,只需添加from __future__ import with_statement

编辑:为了尽早获得控制权,您可以将其拆分为不同的.py文件,并在导入前检查主文件中的兼容性(例如,在__init__.py文件包中):

1
2
3
4
5
6
7
8
9
10
# __init__.py

# Check compatibility
try:
  eval("1 if True else 2")
except SyntaxError:
  raise ImportError("requires ternary support")

# import from another module
from impl import *


在程序周围有一个包装器,它执行以下操作。

1
2
3
4
5
6
7
8
9
10
import sys

req_version = (2,5)
cur_version = sys.version_info

if cur_version >= req_version:
   import myApp
   myApp.run()
else:
   print"Your Python interpreter is too old. Please consider upgrading."

您还可以考虑使用sys.version(),如果您计划遇到使用2.0之前的Python解释器的人,但是您还有一些正则表达式要做。

也许还有更优雅的方式来做到这一点。


尝试

1
2
import platform
platform.python_version()

应该给你一个类似"2.3.1"的字符串。如果这不完全符合您的需求,那么通过"平台"内置模块提供了一组丰富的数据。你想要的应该在某处。


做这个版本比较的最好方法可能是使用sys.hexversion。这一点很重要,因为在所有的Python版本中,比较版本元组并不能得到所需的结果。

1
2
3
4
5
import sys
if sys.hexversion < 0x02060000:
    print"yep!"
else:
    print"oops!"


1
2
3
4
5
6
7
import sys    
# prints whether python is version 3 or not
python_version = sys.version_info.major
if python_version == 3:
    print("is python 3")
else:
    print("not python 3")


Answer from Nykakin at AskUbuntu:

您还可以使用标准库中的platform模块从代码本身检查python版本。

有两个功能:

  • platform.python_version()(返回字符串)。
  • platform.python_version_tuple()(返回元组)。

Python代码

Create a file for example: version.py)

易于检查版本的方法:

1
2
3
4
import platform

print(platform.python_version())
print(platform.python_version_tuple())

您也可以使用eval方法:

1
2
3
4
try:
  eval("1 if True else 2")
except SyntaxError:
  raise ImportError("requires ternary support")

在命令行中运行python文件:

1
2
3
$ python version.py
2.7.11
('2', '7', '11')

通过Windows10上的wamp服务器使用cgi输出python:

Screenshot 2016-11-16 14.39.01 by Suriyaa Kudo

有用的资源

  • https://askubuntu.com/questions/505081/what-version-of-python-do-i-have


为了保持向后兼容,集合成为了Python2.4中的核心语言的一部分。那时我做了这个,这对你也有好处:

1
2
if sys.version_info < (2, 4):
    from sets import Set as set


尽管问题是:如何尽早获得控制以发出错误消息并退出?

我要回答的问题是:如何在启动应用程序之前尽早获得控制以发出错误消息?

我可以用与其他帖子不同的方式回答。似乎到目前为止,答案都是试图从Python内部解决您的问题。

我说,在启动python之前进行版本检查。我知道你的路径是Linux或Unix。不过,我只能为您提供一个Windows脚本。我认为将其适应Linux脚本语法并不难。

下面是2.7版的DOS脚本:

1
2
3
4
5
6
7
8
9
10
@ECHO OFF
REM see http://ss64.com/nt/for_f.html
FOR /F"tokens=1,2" %%G IN ('"python.exe -V 2>&1"') DO ECHO %%H | find"2.7"> Nul
IF NOT ErrorLevel 1 GOTO Python27
ECHO must use python2.7 or greater
GOTO EOF
:Python27
python.exe tern.py
GOTO EOF
:EOF

这不会运行应用程序的任何部分,因此不会引发Python异常。它不会创建任何临时文件或添加任何操作系统环境变量。而且它不会因为版本语法规则的不同而导致应用程序异常。这是不太可能的三个安全接入点。

FOR /F线是关键。

1
FOR /F"tokens=1,2" %%G IN ('"python.exe -V 2>&1"') DO ECHO %%H | find"2.7"> Nul

对于多个Python版本,请签出URL:http://www.fpschultze.de/modules/smartfaq/faq.php?FAQID=17

我的黑客版本:

[ms-script;python版本检查python模块的预启动]网址:http://pastebin.com/aauj91fq


1
2
import sys
sys.version

会得到这样的答案

'2.7.6 (default, Oct 26 2016, 20:30:19)
[GCC 4.8.4]'

这里2.7.6是版本


将以下内容放在文件的最上面:

1
2
3
4
5
import sys

if float(sys.version.split()[0][:3]) < 2.7:
    print"Python 2.7 or higher required to run this code," + sys.version.split()[0] +" detected, exiting."
    exit(1)

然后继续使用普通的python代码:

1
2
3
import ...
import ...
other code...


如上所述,语法错误发生在编译时,而不是运行时。虽然python是一种"解释语言",但python代码实际上并没有被直接解释;它被编译成字节代码,然后被解释。导入模块(如果没有.pyc或.pyd文件形式的已编译版本)时会发生编译步骤,这就是您遇到错误的时候,而不是(非常确切地说)您的代码正在运行时。

您可以推迟编译步骤,让它在运行时发生在一行代码上,如果您想这样做的话,可以使用eval,如上所述,但是我个人更喜欢避免这样做,因为它会导致Python执行可能不必要的运行时编译,一方面,另一方面,它创建了我觉得像代码混乱的东西。(如果您愿意,您可以生成生成代码的代码-从现在起6个月内修改和调试代码的时间非常长。)因此,我建议您采用以下方法:

1
2
3
4
5
import sys
if sys.hexversion < 0x02060000:
    from my_module_2_5 import thisFunc, thatFunc, theOtherFunc
else:
    from my_module import thisFunc, thatFunc, theOtherFunc

…即使只有一个函数使用了较新的语法,而且非常短,我也会这样做。(事实上,我会采取一切合理措施,尽量减少此类功能的数量和规模。我甚至可以用其中的一行语法编写一个类似iftrueaelseb(cond,a,b)的函数。

另一件值得指出的事情(我有点惊讶,还没有人指出)是,虽然早期版本的python不支持类似的代码

1
value = 'yes' if MyVarIsTrue else 'no'

…它确实支持类似

1
value = MyVarIsTrue and 'yes' or 'no'

这是写三元表达式的老方法。我还没有安装python 3,但据我所知,这种"老"方法至今仍然有效,因此如果需要支持使用旧版本的python,您可以自己决定是否值得有条件地使用新语法。


我只是在快速搜索后发现这个问题,同时尝试自己解决这个问题,我已经想出了一个混合基于上述几个建议。

我喜欢devplayer使用包装脚本的想法,但缺点是您最终会为不同的操作系统维护多个包装器,所以我决定用python编写包装器,但使用相同的基本"通过运行exe获取版本"逻辑并提出了这一点。

我认为它应该适用于2.5及以后。到目前为止,我已经在Linux上的2.66、2.7.0和3.1.2以及OS X上的2.6.1进行了测试。

1
2
3
4
5
6
import sys, subprocess
args = [sys.executable,"--version"]

output, error = subprocess.Popen(args ,stdout = subprocess.PIPE, stderr = subprocess.PIPE).communicate()
print("The version is: '%s'"  %error.decode(sys.stdout.encoding).strip("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLMNBVCXZ,.+
"
) )

是的,我知道最后的解码/剥离行是可怕的,但我只是想快速获取版本号。我会改进的。

现在这对我来说已经足够好了,但是如果有人能改进它(或者告诉我为什么它是一个糟糕的想法),那也很酷。


对于独立的python脚本,下面的模块docstring用于强制使用python版本(这里是v2.7.x)的技巧可以工作(在*nix上测试)。

1
2
3
4
5
#!/bin/sh
''''python -V 2>&1 | grep -q 2.7 && exec python -u --"$0" ${1+"$@"}; echo"python 2.7.x missing"; exit 1 # '''

import sys
[...]

这也应该处理缺少的python可执行文件,但它依赖于grep。背景见这里。


我认为最好的方法是测试功能而不是版本。在某些情况下,这是微不足道的,而在其他情况下则不然。

如:

1
2
3
4
try :
    # Do stuff
except : # Features weren't found.
    # Do stuff for older versions.

只要你在使用Try/Except块方面有足够的专一性,你就可以覆盖大部分的基础。


我正在扩展Akhan的优秀答案,它在编译python脚本之前打印出一条有用的消息。

如果要确保脚本使用python 3.6或更高版本运行,请在python脚本顶部添加以下两行:

1
2
#!/bin/sh
''''python3 -c 'import sys; sys.exit(sys.version_info < (3, 6))' && exec python3 -u --"$0" ${1+"$@"}; echo 'This script requires Python 3.6 or newer.'; exit 1 # '''

(注:第二行以四个单引号开始,以三个单引号结束。这可能看起来很奇怪,但不是打字错误。)

此解决方案的优点是,如果使用的是3.6以上的Python版本,那么类似于print(f'Hello, {name}!')的代码不会导致SyntaxError。您将看到这个有用的消息:

1
This script requires Python 3.6 or newer.

当然,这个解决方案只在类Unix的shell上工作,并且只有在直接调用脚本(如:./script.py时)并且设置了适当的执行权限位时才工作。


您可以与sys.hexversionsys.version_info核对。

sys.hexversion不是很人性化的,因为它是十六进制数。sys.version_info是一个元组,所以它更人性化。

使用sys.hexversion检查python 3.6或更新版本:

1
2
3
4
5
import sys, time
if sys.hexversion < 0x30600F0:
    print("You need Python 3.6 or greater.")
    for _ in range(1, 5): time.sleep(1)
    exit()

使用sys.version_info检查python 3.6或更新版本:

1
2
3
4
5
import sys, time
if sys.version_info[0] < 3 and sys.version_info[1] < 6:
    print("You need Python 3.6 or greater.")
    for _ in range(1, 5): time.sleep(1)
    exit()

sys.version_info更人性化,但更具个性。我会重提以东十一〔五〕号,尽管它对人类不太友好。

希望这对你有帮助!


这个怎么样?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys

def testPyVer(reqver):
  if float(sys.version[:3]) >= reqver:
    return 1
  else:
    return 0

#blah blah blah, more code

if testPyVer(3.0) == 1:
  #do stuff
else:
  #print python requirement, exit statement


问题很简单。您检查了版本是否小于2.4,不小于或等于。所以如果python版本是2.4,它不小于2.4。你应该拥有的是:

1
    if sys.version_info **<=** (2, 4):

不是

1
    if sys.version_info < (2, 4):