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 |
(为同事通灵。)
您可以使用
1 2 3 4 | try: eval("1 if True else 2") except SyntaxError: # doesn't have ternary |
另外,在python2.5中可以使用
编辑:为了尽早获得控制权,您可以将其拆分为不同的
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." |
您还可以考虑使用
也许还有更优雅的方式来做到这一点。
尝试
1 2 | import platform platform.python_version() |
应该给你一个类似"2.3.1"的字符串。如果这不完全符合您的需求,那么通过"平台"内置模块提供了一组丰富的数据。你想要的应该在某处。
做这个版本比较的最好方法可能是使用
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_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()) |
您也可以使用
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:
有用的资源- 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异常。它不会创建任何临时文件或添加任何操作系统环境变量。而且它不会因为版本语法规则的不同而导致应用程序异常。这是不太可能的三个安全接入点。
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版本,那么类似于
1 | This script requires Python 3.6 or newer. |
当然,这个解决方案只在类Unix的shell上工作,并且只有在直接调用脚本(如:
您可以与
使用
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() |
使用
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() |
希望这对你有帮助!
这个怎么样?
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): |