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


239

如果我有一个至少需要特定版本Python的Python脚本,那么使用较早版本的Python启动该脚本时,如何正确地正常失败是什么?

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

例如,我有一个使用ternery运算符(2.5中的新增功能)和“ with”块(2.6中的新增功能)的程序。我写了一个简单的解释程序版本检查程序,这是脚本会调用的第一件事……只是它没有那么远。相反,脚本在python编译期间失败,甚至没有调用我的例程。因此,脚本用户会看到一些非常模糊的synax错误回溯-这几乎需要专家来推断,这只是运行错误版本的Python的情况。

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

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下运行时,我想要这个结果

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

而不是这个结果:

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

(引导同事。)


3
“请检查python的版本。问题是某些语法在python的旧版本中是非法的。” 我不知道这是怎么个问题。如果可以检查版本,则可以避免语法错误。版本检查如何不适用于语法?你能澄清你的问题吗?
S.Lott,

4
@ S.Lott不,您没有错,只是困难在于将代码包含在也不会被读取(分析)和不执行的地方—答案显示,这并不立即可见。
布伦丹

7
S.Lott,您无法在旧版本的python中执行测试,因为它无法编译。相反,您会收到一个通用语法错误。使用2.4解释器尝试示例代码,您会发现无法进行版本测试。
Mark Harrison

7
@ S.Lott好吧,这取决于您认为琐碎的事-就我个人而言,我不会考虑为不同版本的Python创建单独的文件或生成琐碎的额外进程。我想说这个问题很有价值,尤其是当您认为Python充满了整洁且常常令人惊讶的技巧时–我来自Google,想知道是否有一个整洁的答案
Brendan

7
我认为我们的讨论已经结束。我问了一个我不知道该怎么做的问题,得到了一个答案,告诉我该怎么做。我没有提出任何建议,我只是接受了orip的答案,这对我(实际上是我所引导的同事)非常有用。Viva Le Stack溢出!
马克·哈里森

Answers:


111

您可以使用进行测试eval

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

另外,with 可以在Python 2.5,只需添加from __future__ import with_statement

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

# __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 *

10
这是一个了不起的答案。需要解决的问题的主要问题是,对于该版本的python,程序必须在语法上正确才能开始执行,因此,使用新语法会使程序无法从较旧版本的解释器启动。评估工作
-Autoplectic

7
如果该软件包是由setuptools安装的,则字节编译源文件将失败。而且,所有产生运行时错误消息的扭曲似乎都没有意义-为什么不仅仅记录需求并保留需求呢?
约翰·马钦

2
请注意,如果尝试检查表达式而不是简单的语句,则需要使用exec代替eval。我在尝试编写一个可以在py2k和py3k中打印到stderr的函数时遇到了这个问题。
熊加米奥夫

2
我认为此解决方案的一个更干净的版本是将“检查”放在一个单独的模块中,然后导入该模块(将import站包裹在try / except中)。请注意,您可能还需要检查其他内容SyntaxError(例如,内置函数或对标准库的添加)
Steven

103

围绕您的程序执行以下操作的包装器。

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解释器的人员,那么您也可以考虑使用,但是您需要做一些正则表达式。

并且可能会有更优雅的方法来做到这一点。


7
仅供参考,“ cur_version> = req_version”应作为条件。
orip

4
sys.version_info不是功能。
nh2

3
将代码放入这样的成功条件中是非常糟糕的做法,因为这是不必要的缩进和逻辑添加。只是做一个:如果sys.version_info [:2] <req_version:打印“旧”;sys.exit()-否则照常继续。
timss 2013年

1
就像蒂姆·彼得斯(Tim Peters)在《 Python的禅宗》中说的那样:“扁平比嵌套更好。” (您可以通过在python中键入“ import this”来看到此内容)
Christopher Shroba 2015年

1
@ChristopherShroba谢谢你import this。一个可爱的转移。
塞缪尔·哈默

32

尝试

导入平台
platform.python_version()

应该给你一个像“ 2.3.1”这样的字符串。如果这不完全符合您的要求,则可以通过“平台”内置的一组丰富的数据。您想要的东西应该在某个地方。


4
-1:这不起作用,如更新的问题中所述。如果您使用Python较新版本中的任何语法,则文件将无法编译,如果未编译,则文件将无法运行并检查版本!
Scott Griffiths 2012年

1
@ScottGriffiths运行print(platform.python_version())代替platform.python_version()
Suriyaa '16

@ScottGriffiths还可以查看我的答案:stackoverflow.com/a/40633458/5157221
Suriyaa '16

22

进行此版本比较的最佳方法可能是使用sys.hexversion。这很重要,因为比较版本元组不会在所有python版本中都提供所需的结果。

import sys
if sys.hexversion < 0x02060000:
    print "yep!"
else:
    print "oops!"

我认为这是最优雅的方法,但可能不是其他开发人员最容易理解的方法。
尼克·博尔顿

5
您能否解释在什么情况下比较版本元组将无法获得预期的结果?
SpoonMeiser 2012年

版本元组也可以包含字母数字值。
索林2012年

8
-1:这不起作用,如更新的问题中所述。如果您使用Python较新版本中的任何语法,则文件将无法编译,如果未编译,则文件将无法运行并检查版本!
Scott Griffiths

在哪个版本/平台上会失败?
索林

15
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")

7
请注意,在python 2.6及更低版本中,sys.version_info不是命名元组。您需要使用sys.version_info[0]主要版本号和sys.version_info[1]次要版本号。
coredumperror 2015年

9

Nykakin在AskUbuntu的回答:

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

有两个功能:

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

Python代码

创建例如文件:version.py

简单的版本检查方法:

import platform

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

您也可以使用以下eval方法:

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

在命令行中运行Python文件:

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

Windows 10上的WAMP服务器通过CGI输出的Python输出:

屏幕截图2016-11-16 14.39.01通过Suriyaa Kudo


有用的资源


7

为了保持向后兼容,集成为Python 2.4核心语言的一部分。那时我做到了,这也将为您工作:

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

3
最好检查功能而不是版本,不是吗?try: set except NameError: from sets import Set as set
Orip 2011年

@orip:为什么?如果您知道引入哪个版本的功能(如此处的设置),则只需使用上面的代码即可。没有错。
安德烈(André)

7

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

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

与其他帖子相比,我的回答有很大不同。到目前为止,似乎有答案正在尝试从Python内部解决您的问题。

我说,在启动Python之前先进行版本检查。我看到您的路径是Linux或UNIX。但是,我只能为您提供Windows脚本。我形象地将其适应Linux脚本语法并不难。

这是2.7版的DOS脚本:

@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异常。它不会创建任何临时文件或添加任何OS环境变量。由于版本语法规则不同,它不会使您的应用程序异常终止。这是三个较少的安全访问点。

FOR /F线是关键。

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

和我的hack版本:

[MS脚本;Python模块的Python版本检查预启动] http://pastebin.com/aAuJ91FQ


对于那些反对票,请不要害怕解释原因。
DevPlayer 2012年

正是我在寻找。谢谢!%% H是多少?
Clocker

@Clocker python.exe -V将返回“ Python 2.7”字符串。控制台将“ Python”字符串放入%% G中,并将“ 2.7”字符串放入自动创建的操作系统变量%% H(G之后的下一个字母)中。回声%% H | 在DOS命令中找到“ 2.7”管道“ 2.7”,如果在“ 2.7”中找到%% H,则将“ 2.7”设置为1。使用DOS find命令导致的错误级别为1,这将使我们能够分支到DOS批处理标签:Python27
DevPlayer 2016年

3
import sys
sys.version

将会得到这样的答案

'2.7.6(默认值,2016年10月26日,20:30:19)\ n [GCC 4.8.4]'

这里2.7.6是版本


2

如上所述,语法错误发生在编译时,而不是运行时。虽然Python是一种“解释语言”,但实际上并没有直接解释Python代码。它被编译为字节码,然后被解释。导入模块时会发生一个编译步骤(如果没有可用的.pyc或.pyd文件形式的已编译版本),那就是您遇到错误的时候,而不是(完全正确)何时您的代码正在运行。

如上所述,您可以通过使用eval来推迟编译步骤,并使其在运行时针对单行代码发生,但是我个人更希望避免这样做,因为这可能导致Python潜在地执行不必要的运行时编译,一方面,另一方面,它使我感到代码混乱。(如果需要,您可以生成代码,再生成代码,然后再生成代码,并在从现在起的6个月内花费大量的时间进行修改和调试。)因此,我建议使用的是这样的内容:

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的早期版本不支持类似

value = 'yes' if MyVarIsTrue else 'no'

..它确实支持如下代码

value = MyVarIsTrue and 'yes' or 'no'

那是三元表达式的写法。我尚未安装Python 3,但据我所知,这种“旧”方式至今仍然有效,因此您可以自行决定是否有条件地使用新语法是否值得支持使用旧版本的Python。


2
认真吗 只是复制代码,以便您可以更改一些较小的结构?uck 真恶心 至于a and b or c代替b if a else c,它不等同; 如果b是虚假的,它将失败,a而不是产生b
克里斯·摩根

1
我不建议重复代码,我建议为特定于版本的代码创建包装函数,其签名在各个版本之间均不变,并将这些函数放入特定于版本的模块中。我说的是可能长达1至5行的函数。的确,在b可能为假的情况下,如果a else c则a和b或c与b不同。因此,我想如果common_ops_2_4.py中的AThenBElseC(a,b,c)必须是2或3行而不是1行。这种方法实际上是通过将常用的习惯用法封装到函数中来减少代码的。
沙威

2

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

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代码:

import ...
import ...
other code...

1
宁可使用sys.version_info < (2, 7)
Antti Haapala

@AnttiHaapala这对我来说很完美,您能解释一下为什么将sys.version_info类型与元组进行比较吗?
jjj

@jjj sys.version_info 曾经是一个元组;例如(2, 4, 6, 'final', 0); 仅在Python 3和2.7中,它才更改为单独的类型,但仍可与元组媲美。
安蒂·哈帕拉

@AnttiHaapala我比我更喜欢这种方法...谢谢!
jml

1

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

例如:

try :
    # Do stuff
except : # Features weren't found.
    # Do stuff for older versions.

只要您对使用try / except块有足够的专业知识,就可以覆盖大多数基础知识。


2
你是对的。这就是他的要求-有时对版本Y中的功能进行测试甚至无法编译为版本X中的字节码,因此无法直接完成。
Orip

1

在尝试自己解决问题时,我经过快速搜索后才发现此问题,我根据上述一些建议提出了一种混合解决方案。

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

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

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,.+ \n") )

是的,我知道最后的解码/带状线很可怕,但是我只是想快速获取版本号。我将对此进行完善。

目前,这对于我来说已经足够好了,但是如果有人可以改进它(或者告诉我为什么这是一个糟糕的主意),那也很酷。


1

对于 独立的 python脚本,以下用于强制执行python版本(此处为v2.7.x)的模块docstring技巧适用(在* nix上测试)。

#!/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有依赖性。有关背景,请参见此处


0

您可以使用sys.hexversion或进行检查sys.version_info

sys.hexversion 不是很友好,因为它是一个十六进制数。 sys.version_info是一个元组,因此更加人性化。

使用以下命令检查Python 3.6或更高版本sys.hexversion

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

使用以下命令检查Python 3.6或更高版本sys.version_info

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更加人性化,但需要更多字符。我会推荐sys.hexversion,即使它不那么人性化。

希望对您有所帮助!


0

我正在扩展akhan的出色答案,该答案甚至在Python脚本编译之前就打印出了有用的信息。

如果要确保该脚本正在使用Python 3.6或更高版本运行,请将这两行添加到Python脚本的顶部:

#!/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 # '''

(注意:第二行以四个单引号开头,以三个单引号结尾单引号。这看起来很奇怪,但这不是一个错字。)

该解决方案的优势在于,如果使用的Python版本早于3.6,则print(f'Hello, {name}!')不会导致类似的代码SyntaxError。您会看到以下有用信息:

This script requires Python 3.6 or newer.

当然,该解决方案仅在类Unix的shell上有效,并且仅在脚本被直接调用(例如./script.py:)并且设置了适当的eXecute权限位时才有效。


-2

这个怎么样:

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

5
-1:这不起作用,如更新的问题中所述。如果您使用Python较新版本中的任何语法,则文件将无法编译,如果未编译,则文件将无法运行并检查版本!
Scott Griffiths 2012年

-3

问题很简单。您检查了版本是否小于 2.4,不小于或等于。因此,如果Python版本是2.4,则它不少于2.4。您应该拥有的是:

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

,不

    if sys.version_info < (2, 4):

4
阅读第3段和更新内容。您不会执行该代码,因为如果您使用新的语言构造,则您的代码将无法在2.4上编译。
马克·哈里森

小于小于还好,只是错过了评估。
克雷格
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.