How to write Python code that is able to properly require a minimal python version?

后端 未结 9 760
面向向阳花
面向向阳花 2021-01-31 01:33

I would like to see if there is any way of requiring a minimal python version.

I have several python modules that are requiring Python 2.6 due to the new exception handl

相关标签:
9条回答
  • 2021-01-31 02:01

    I used to have a more complicated approach for supporting both Python2 and Python3, but I no longer try to support Python2, so now I just use:

    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"
    

    If the version check fails, you get a traceback with something like:

    AssertionError: requires Python 3.7 or newer
    

    at the bottom.

    0 讨论(0)
  • 2021-01-31 02:01

    I'm guessing you have something like:

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

    but module_foo requires a particular version as well? This being the case, it is perfectly valid to rearrange your code thus:

    import sys
    # check sys.version
    import module_foo
    

    Python does not require that imports, aside from from __future__ import [something] be at the top of your code.

    0 讨论(0)
  • 2021-01-31 02:07

    I need to make sure I'm using Python 3.5 (or, eventually, higher). I monkeyed around on my own and then I thought to ask SO - but I've not been impressed with the answers (sorry, y'all ::smile::). Rather than giving up, I came up with the approach below. I've tested various combinations of the min_python and max_python specification tuples and it seems to work nicely:

    • Putting this code into a __init__.py is attractive:

      • Avoids polluting many modules with a redundant version check
      • Placing this at the top of a package hierarchy even more further supports the DRY principal, assuming the entire hierarchy abides by the same Python version contraints
      • Takes advantage of a place (file) where I can use the most portable Python code (e.g. Python 1 ???) for the check logic and still write my real modules in the code version I want
      • If I have other package-init stuff that is not "All Python Versions Ever" compatible, I can shovel it into another module, e.g. __init_p3__.py as shown in the sample's commented-out final line. Don't forget to replace the pkgname place holder with the appropriate package name.
    • If you don't want a min (or max), just set it to = ()

    • If you only care about the major version, just use a "one-ple", e.g. = (3, ) Don't forget the comma, otherwise (3) is just a parenthesized (trivial) expression evaluating to a single int
    • You can specify finer min/max than just one or two version levels, e.g. = (3, 4, 1)
    • There will be only one "Consider running as" suggestion when the max isn't actually greater than the min, either because max is an empty tuple (a "none-ple"?), or has fewer elements.

    NOTE: I'm not much of a Windoze programmer, so the text_cmd_min and text_cmd_max values are oriented for *Nix systems. If you fix up the code to work in other environments (e.g. Windoze or some particular *Nix variant), then please post. (Ideally, a single super-smartly code block will suffice for all environments, but I'm happy with my *Nix only solution for now.)

    PS: I'm somewhat new to Python, and I don't have an interpreter with version less than 2.7.9.final.0, so it's tuff to test my code for earlier variants. On the other hand, does anyone really care? (That's an actual question I have: In what (real-ish) context would I need to deal with the "Graceful Wrong-Python-Version" problem for interpreters prior to 2.7.9?)

    __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__
    
    0 讨论(0)
  • 2021-01-31 02:08

    You should not use any Python 2.6 features inside the script itself. Also, you must do your version check before importing any of the modules requiring a new Python version.

    E.g. start your script like so:

    #!/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
    
    0 讨论(0)
  • 2021-01-31 02:12

    To complement the existing, helpful answers:

    You may want to write scripts that run with both Python 2.x and 3.x, and require a minimum version for each.

    For instance, if your code uses the argparse module, you need at least 2.7 (with a 2.x Python) or at least 3.2 (with a 3.x Python).

    The following snippet implements such a check; the only thing that needs adapting to a different, but analogous scenario are the MIN_VERSION_PY2=... and MIN_VERSION_PY3=... assignments.

    As has been noted: this should be placed at the top of the script, before any other import statements.

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

    If the version requirements aren't met, something like the following message is printed to stderr and the script exits with exit code 1.

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

    Note: This is a substantially rewritten version of an earlier, needlessly complicated answer, after realizing - thanks to Arkady's helpful answer - that comparison operators such as > can directly be applied to tuples.

    0 讨论(0)
  • 2021-01-31 02:13

    You can take advantage of the fact that Python will do the right thing when comparing tuples:

    #!/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)
    
    0 讨论(0)
提交回复
热议问题