245

如果我有一个至少需要特定版本的 Python 的 Python 脚本,那么当使用早期版本的 Python 启动脚本时,优雅地失败的正确方法是什么?

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

例如,我有一个程序使用三元运算符(2.5 中的新功能)和“with”块(2.6 中的新功能)。我写了一个简单的小解释器版本检查程序,这是脚本首先调用的……除了它没有那么远。相反,脚本在 python 编译期间失败,甚至在我的例程被调用之前。因此,脚本的用户会看到一些非常模糊的语法错误回溯——这几乎需要专家来推断这只是运行错误版本的 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

(为同事通灵。)

4

18 回答 18

115

您可以使用以下方法进行测试eval

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

此外,在 Python 2.5with 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 *
于 2009-01-15T08:50:17.690 回答
107

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

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 解释器的人,但是您需要做一些正则表达式。

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

于 2009-01-15T09:26:51.143 回答
34

尝试

进口平台
platform.python_version()

应该给你一个像“2.3.1”这样的字符串。如果这不是您想要的,那么可以通过“平台”内置提供丰富的数据集。你想要的东西应该在某个地方。

于 2009-01-15T08:55:44.357 回答
22

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

import sys
if sys.hexversion < 0x02060000:
    print "yep!"
else:
    print "oops!"
于 2010-06-28T12:38:59.043 回答
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")
于 2015-06-25T13:39:04.657 回答
9

来自 AskUbuntu 的Nykakin的回答:

您还可以使用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:

Suriyaa Kudo 截屏 2016-11-16 14.39.01


有用的资源

于 2016-11-16T13:31:45.263 回答
7

Sets 成为 Python 2.4 中核心语言的一部分,以保持向后兼容。我当时这样做了,这也对你有用:

if sys.version_info < (2, 4):
    from sets import Set as set
于 2009-01-15T09:43:38.877 回答
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 异常。它不会创建任何临时文件或添加任何操作系统环境变量。由于版本语法规则不同,它不会使您的应用程序出现异常。这是三个不太可能的安全访问点。

FOR /F线是关键。

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

对于多个 python 版本,请查看网址: http ://www.fpschultze.de/modules/smartfaq/faq.php?faqid=17

还有我的破解版:

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

于 2011-10-04T01:52:20.950 回答
3
import sys
sys.version

会得到这样的答案

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

这里2.7.6是版本

于 2017-05-24T05:26:49.613 回答
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。

于 2011-09-13T02:27:02.737 回答
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...
于 2015-10-23T11:19:29.907 回答
1

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

例如:

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

只要您在使用 try/except 块方面足够具体,您就可以覆盖大部分基础。

于 2009-01-15T08:51:22.290 回答
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") )

是的,我知道最终的解码/剥离线很糟糕,但我只是想快速获取版本号。我要完善它。

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

于 2012-05-09T16:11:57.753 回答
1

对于独立的 python 脚本,以下模块文档字符串技巧来强制执行 python 版本(这里是 v2.7.x)有效(在 *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。有关背景,请参见此处

于 2018-07-15T21:40:57.273 回答
1

我正在扩展 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 # '''

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

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

This script requires Python 3.6 or newer.

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

于 2018-12-11T23:57:43.587 回答
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,即使它不那么人性化。

我希望这对你有帮助!

于 2018-01-27T19:26:55.413 回答
-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
于 2009-09-14T22:00:06.313 回答
-3

问题很简单。您检查了版本是否小于2.4,不小于或等于. 所以如果Python版本是2.4的话,是不会低于2.4的。你应该有的是:

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

, 不是

    if sys.version_info < (2, 4):
于 2010-04-09T19:31:02.303 回答