【问题标题】:How to write Python code that is able to properly require a minimal python version?如何编写能够正确要求最小 Python 版本的 Python 代码?
【发布时间】:2011-06-03 08:29:54
【问题描述】:

我想看看是否有任何方法需要最小的 python 版本。

由于新的异常处理(as 关键字),我有几个需要 Python 2.6 的 Python 模块。

看起来即使我在脚本开头检查 python 版本,代码也不会运行,因为解释器会在模块内部失败,抛出一个丑陋的系统错误,而不是告诉用户使用更新的 python。

【问题讨论】:

标签: python


【解决方案1】:

您可以利用 Python 在比较元组时会做正确的事情这一事实:

#!/usr/bin/python
import sys
MIN_PYTHON = (2, 6)
if sys.version_info < MIN_PYTHON:
    sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON)

【讨论】:

    【解决方案2】:

    您不应在脚本本身中使用任何 Python 2.6 功能。此外,在导入任何需要新 Python 版本的模块之前,您必须进行版本检查。

    例如像这样开始你的脚本:

    #!/usr/bin/env python
    import sys
    
    if sys.version_info[0] != 2 or sys.version_info[1] < 6:
        print("This script requires Python version 2.6")
        sys.exit(1)
    
    # rest of script, including real initial imports, here
    

    【讨论】:

    • +1。我在您的print 中添加了一些括号,因为否则正如 Tim 指出的那样,它会在 Python 3 中使用SyntaxError 失败(这很棘手!)我认为这是最正确的方法 - 我的方法更快但有点小技巧。
    • 我认为它没有必要在 Python 3 中工作。毕竟,Python 3 通常与 Python 2.x 的源代码不兼容。由于需要为几乎任何东西分发单独的 Python 2.x 和 3.x 版本,您可以完全退出 Python 3.x 版本。
    • 即使在尝试使用 3.x 运行脚本时也可以优雅地退出,但除非 整个 > 脚本恰好是_syntactically_有效的3.x。 print 不是打印错误消息的好选择,因为它默认打印到 stdout;简单的解决方案是使用 sys.exit("This script requires Python 2.x, &gt;= 2.6") (如在接受的答案中),它打印到 stderr 并以代码 1 退出。(顺便说一句:在 Windows 上,使用 pythonw.exe 调用时,您必须做额外的工作才能使错误浮出水面。)
    【解决方案3】:

    从版本 9.0.0 开始 pip supports Requires-Python 分布元数据中的字段,可以由从版本 24-2-0 开始的 setuptools 编写。此功能通过python_requires 关键字参数提供给setup 函数。

    示例(在 setup.py 中):

    setup(
    ...
       python_requires='>=2.5,<2.7',
    ...
    
    )
    

    要利用此功能,如果尚未打包项目/脚本,则必须先打包。这在典型情况下非常容易,但应该这样做,因为它允许用户轻松安装、使用和卸载给定的项目/脚本。详情请见Python Packaging User Guide

    【讨论】:

    【解决方案4】:
    import sys
    if sys.hexversion < 0x02060000:
        sys.exit("Python 2.6 or newer is required to run this program.")
    
    import module_requiring_26
    

    另外一个很酷的部分是它可以包含在__init__ 文件或模块中。

    【讨论】:

    • 您不想防止在 Python 3 下运行脚本吗?
    • 这是 Walter Mundt 答案的劣质版本,在此之前已发布。
    • 不如@Arkady 的 "sys.version_info
    【解决方案5】:

    我曾经有一个更复杂的方法来支持Python2和Python3,但我不再尝试支持Python2,所以现在我只使用:

    import sys
    MIN_PYTHON = (3, 7)
    assert sys.version_info >= MIN_PYTHON, f"requires Python {'.'.join([str(n) for n in MIN_PYTHON])} or newer"
    

    如果版本检查失败,您将获得类似以下内容的回溯:

    AssertionError: requires Python 3.7 or newer
    

    在底部。

    【讨论】:

    • 老版本看不懂f-strings,所以我更喜欢print-f格式"requires Python %s.%s or newer." % MIN_PYTHON
    • 使用 f-strings 的 python 脚本甚至不会为低于 3.6 的 python 版本编译 - 因此没有代码会看到执行,因为编译错误发生得更早。只有包装脚本才能以编程方式和未受过教育的用户兼容方式解决它。
    • 当然,使用旧式格式将使测试适用于旧版本。我没有这样做,因为我的 Python 源代码的其余部分都有 f 字符串,因此仅为该测试使用旧式格式仍然不允许文件编译和执行检查。我必须完全避免使用 f 字符串,如果我在 3.6 之前关心 Python,我会这样做。我自己对 Python 版本要求的所有测试都是针对更新版本的功能。
    【解决方案6】:

    补充现有的有用答案:

    您可能希望编写脚本同时运行 Python 2.x 3.x,并且至少需要每个的版本。

    例如,如果您的代码使用 argparse 模块,则您至少需要 2.7(使用 2.x Python)至少需要 3.2(使用 3.x Python)。

    下面的sn -p 实现了这样的检查;唯一需要适应不同但类似情况的是MIN_VERSION_PY2=...MIN_VERSION_PY3=... 分配。

    如前所述:这应该放在脚本顶部,在任何其他import 语句之前

    import sys
    
    MIN_VERSION_PY2 = (2, 7)    # min. 2.x version as major, minor[, micro] tuple
    MIN_VERSION_PY3 = (3, 2)    # min. 3.x version
    
    # This is generic code that uses the tuples defined above.
    if (sys.version_info[0] == 2 and sys.version_info < MIN_VERSION_PY2
          or
        sys.version_info[0] == 3 and sys.version_info < MIN_VERSION_PY3):
          sys.exit(
            "ERROR: This script requires Python 2.x >= %s or Python 3.x >= %s;"
            " you're running %s." % (
              '.'.join(map(str, MIN_VERSION_PY2)), 
              '.'.join(map(str, MIN_VERSION_PY3)), 
              '.'.join(map(str, sys.version_info))
            )
          )
    

    如果不满足版本要求,则会将以下消息打印到 stderr 并且脚本以退出代码 1 退出。

    This script requires Python 2.x >= 2.7 or Python 3.x >= 3.2; you're running 2.6.2.final.0.
    

    注意:这是对早期不必要的复杂答案的大幅改写版本,在意识到 - 感谢Arkady's helpful answer - 可以将比较运算符(如&gt;)直接应用于元组之后。

    【讨论】:

      【解决方案7】:

      我猜你有类似的东西:

      import module_foo
      ...
      import sys
      # check sys.version
      

      但是 module_foo 也需要特定的版本?在这种情况下,重新排列代码是完全有效的:

      import sys
      # check sys.version
      import module_foo
      

      Python 不要求导入,除了 from __future__ import [something] 位于代码的顶部。

      【讨论】:

      • “Python 不要求导入 [...] 位于代码的顶部。”没错,但是 PEP 8 说:“导入总是放在文件的顶部,就在任何模块 cmets 和文档字符串之后,模块全局变量和常量之前”;因此,如果您不将导入放在顶部,大多数代码 linter 都会抱怨。尽管如此,AFAIK 在这种情况下是不可避免的。
      【解决方案8】:

      我需要确保我使用的是 Python 3.5(或最终更高版本)。我自己胡闹,然后我想问SO - 但我对答案没有印象(对不起,你们都是::smile::) .我没有放弃,而是想出了下面的方法。我已经测试了 min_pythonmax_python 规范元组的各种组合,它似乎工作得很好:

      • 将此代码放入__init__.py 很有吸引力:

        • 通过冗余版本检查避免污染许多模块
        • 假设整个层次结构遵循相同的 Python 版本约束,将其放置在包层次结构的顶部会进一步支持 DRY 原则
        • 利用一个地方(文件),我可以在其中使用最便携的 Python 代码(例如 Python 1 ???)进行检查逻辑,并且仍然编写我的 real 我想要的代码版本中的模块
        • 如果我有其他与“所有 Python 版本”不兼容的 package-init 内容,我可以将其放入另一个模块中,例如__init_p3__.py 如示例中注释掉的最后一行所示。 不要忘记将 pkgname 占位符替换为适当的包名称。
      • 如果您不想要最小值(或最大值),只需将其设置为 = ()

      • 如果您只关心主要版本,只需使用 "one-ple",例如= (3, ) 不要忘记逗号,否则 (3) 只是一个括号(普通)表达式,计算结果为单个 int
      • 您可以指定比仅一个或两个版本级别更精细的最小值/最大值,例如= (3, 4, 1)
      • 当 max 实际上不大于 min 时,将只有一个“考虑运行为”建议,因为 max 是一个空元组(“none-ple”?),或者具有较少的元素。李>

      注意:我不是一个 Windoze 程序员,所以 text_cmd_mintext_cmd_max 值面向 *Nix 系统。如果您修复代码以在其他环境中工作(例如 Windoze 或某些特定的 *Nix 变体),请发布。 (理想情况下,一个超级智能的代码块足以满足所有环境,但我对我目前唯一的 *Nix 解决方案感到满意。)

      PS:我对 Python 有点陌生,而且我没有版本低于 2.7.9.final.0 的解释器,所以测试我的代码以获取早期变体是很困难的.另一方面,真的有人在乎吗? (这是我遇到的一个实际问题:在 2.7.9 之前的解释器中,我需要在什么(实际)上下文中处理“Graceful Wrong-Python-Version”问题?) p>

      __init__.py

      '''Verify the Python Interpreter version is in range required by this package'''
      
      min_python = (3, 5)
      max_python = (3, )
      
      import sys
      
      if (sys.version_info[:len(min_python)] < min_python) or (sys.version_info[:len(max_python)] > max_python):
      
          text_have = '.'.join("%s" % n for n in sys.version_info)
          text_min  = '.'.join("%d" % n for n in min_python) if min_python else None
          text_max  = '.'.join("%d" % n for n in max_python) if max_python else None
      
          text_cmd_min = 'python' + text_min + '  ' + "  ".join("'%s'" % a for a in sys.argv) if min_python              else None
          text_cmd_max = 'python' + text_max + '  ' + "  ".join("'%s'" % a for a in sys.argv) if max_python > min_python else None
      
          sys.stderr.write("Using Python version: " + text_have + "\n")
      
          if min_python:  sys.stderr.write(" - Min required: " + text_min + "\n")
          if max_python:  sys.stderr.write(" - Max allowed : " + text_max + "\n")
          sys.stderr.write("\n")
      
          sys.stderr.write("Consider running as:\n\n")
          if text_cmd_min:  sys.stderr.write(text_cmd_min + "\n")
          if text_cmd_max:  sys.stderr.write(text_cmd_max + "\n")
          sys.stderr.write("\n")
      
          sys.exit(9)
      
      # import pkgname.__init_p3__
      

      【讨论】:

        【解决方案9】:

        而不是索引你总是可以这样做,

        import platform
        if platform.python_version() not in ('2.6.6'):
            raise RuntimeError('Not right version')
        

        【讨论】:

        • 这对特定版本进行硬编码,而问题是关于最低版本检查。
        猜你喜欢
        • 2017-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-24
        • 2020-11-17
        • 2021-04-11
        • 2014-07-12
        • 1970-01-01
        相关资源
        最近更新 更多