由于旧的解释器版本,如何妥善处理失败的将来功能(__future__)导入?


69

您如何优雅地处理将来失败的功能导入?如果用户使用Python 2.5运行并且模块中的第一条语句是:

from __future__ import print_function

针对Python 2.5编译此模块将失败,并显示以下内容:

  File "__init__.py", line 1
    from __future__ import print_function
SyntaxError: future feature print_function is not defined

我想通知用户他们需要使用Python> = 2.6重新运行该程序,并可能提供一些有关如何执行此操作的说明。但是,引用PEP 236

只能在future_statement之前出现的行是:

  • 模块文档字符串(如果有)。
  • 评论。
  • 空白行。
  • 其他future_statement。

所以我不能做这样的事情:

import __future__

if hasattr(__future__, 'print_function'):
    from __future__ import print_function
else:
    raise ImportError('Python >= 2.6 is required')

因为它产生:

  File "__init__.py", line 4
    from __future__ import print_function
SyntaxError: from __future__ imports must occur at the beginning of the file

PEP的此片段似乎给内联完成的希望:

问:我想将future_statements包装在try / except块中,因此我可以根据运行的Python版本使用不同的代码。我为什么不能

抱歉!try / except是运行时功能;future_statements主要是编译时的mm头,并且try / except会在编译器完成很长时间之后发生。也就是说,在您尝试/例外时,对模块有效的语义已经完成。由于try / except无法完成看起来 应该完成的工作,因此完全不允许这样做。我们还希望使这些特殊声明易于查找和识别。

请注意,您可以直接导入__future__,并使用其中的信息以及sys.version_info来确定相对于给定功能的状态,您正在运行的发行版所处的位置。

有想法吗?


3
我只是碰到了这个问题,而且我不停地问自己:如果“ future_statements主要是编译时的mm头”,那么Python为什么不引入呢? #ifdef作为编译时的mm头,因此我可以__future__在一个步骤中优雅地处理所有这些问题。文件?AAAARRGGGHHH .....
sdaau

您可以使用简单 if在运行时。也用于import
rundekugel '16

Answers:


60

“我想通知用户他们需要使用Python> = 2.6重新运行该程序,并可能提供一些有关如何执行此操作的说明。”

这不是自述文件的目的吗?

这是您的替代选择。“包装器”:一点点Python,可在运行目标aop之前检查环境。

文件:appwrapper.py

import sys
major, minor, micro, releaselevel, serial = sys.version_info
if (major,minor) <= (2,5):
    # provide advice on getting version 2.6 or higher.
    sys.exit(2)
import app
app.main()

“直接导入”是什么意思。您可以检查的内容__future__。您仍然受制于from __future__ import print_function向编译器提供a信息这一事实,但是您可以在导入完成实际工作的模块之前先三思。

import __future__, sys
if hasattr(__future__, 'print_function'): 
    # Could also check sys.version_info >= __future__. print_function.optional
    import app
    app.main()
else:
    print "instructions for upgrading"

也许测试“如果major <2或major == 2且minor <= 5”,否则在假设的1.7版本中不会失败。
格雷格(Greg Hewgill)

20
甚至更好,“如果(主要,次要)<=(2,5)”
Greg Hewgill'Dec 23'08

10
为什么不为什么“如果sys.version_info [:2] <=(2,5)”更重要的是,我认为,我不喜欢创建仅使用一次的变量(或者根本不使用“ minor”之后的所有变量)例如)
的JürgenA.艾哈德

@JürgenA.Erhard我认为一般来说,中间变量可以使您更清楚地了解自己在做什么,因为只有在不会真正造成伤害的情况下才可以这样做。
porglezomp

3
52次投票,没有人指出这'print_function' in __future__实际上没有用...应该是hasattr(__future__, 'print_function')
Antti Haapala 2015年

47

我以前使用的一种相当狡猾但简单的方法是利用以下事实:在python 2.6中引入了字节常量,并在文件开头附近使用了类似的方法:

b'This module needs Python 2.6 or later. Please do xxx.'

在Python 2.6或更高版本中这是无害的,但SyntaxError在任何较早版本中都是。任何尝试编译您的文件的人仍然会遇到错误,但是他们也会得到您想要提供的任何消息。

您可能会认为,由于必须在该行之后from __future__ import print_function加上该行,所以导入将生成,SyntaxError并且您将看不到有用的错误消息,但奇怪的是,后面的错误优先。我怀疑由于导入中的错误本身并不是语法错误,因此不会在第一次编译时就引发,因此真正的语法错误会首先引发(但我猜是)。

这可能不符合您“优美”的标准,并且它非常特定于python 2.6,但是它快速且容易实现。


6
优雅!我在shebang行下没有分配就使用了它:b'This script requires Python 2.6, this line is a SyntaxError in earlier versions'这对于用户读取异常跟踪和程序员读取源代码来说是一条非常清晰的消息。
RobM 2010年

@RobM:很高兴您喜欢它,您说对了,不需要分配作业-我将其删除。
Scott Griffiths

1
我认为这有效的原因是因为它将第一个字符串文字解释为该模块的文档字符串,这是在“来自未来”语句之前唯一允许的事情。我当时使用的是这种聪明的技术,后来发现当我添加适当的文档字符串时,它不再起作用了,因为那样一来,它就变成了“来自未来”之前的第二个字符串文字。我的解决办法是把通知有关Python 2.6友善的语言为文档字符串的最后一行,用“”,”尾部在同一行,这使得它仍然在SyntaxError错误显示出来,并且在文档字符串无害的信息。
Ivan X

40

只需在与的同一行中添加评论"from __future__ import ...",如下所示:

from __future__ import print_function, division  # We require Python 2.6 or later

由于Python显示包含错误的行,因此,如果您尝试使用Python 2.5运行模块,则会得到一个漂亮的描述性错误:

    from __future__ import print_function, division  # We require Python 2.6 or later
SyntaxError: future feature print_function is not defined
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.