Search code examples
pythonpython-3.xpython-2.7

Is sys.version_info reliable for Python version checking?


If I'm making a module that I would like to run the same in Python 2 as in Python 3, there are a ton of options including six, futures, and 2to3. If the number of changes is small though, each of those tools has enough quirks that I tend to prefer to just write a compatibility interface for the few incompatible functions my module actually uses.

A reasonably standard way to accomplish that is with a straightforward version check.

import module_bar

if sys.version_info >= (3,):
    uniformly_named_foo = module_bar.py3_thing
else:
    uniformly_named_foo = module_bar.py2_thing

Are there any oddball cases where sys.version_info wouldn't be reported correctly though? I've been bitten enough in the past by malformed paths, configs, installations, modifications, and whatnot that this doesn't feel like a thing I should trust.

When we get right down to it, what I actually care about is if a particular feature is implemented. In web development, it's generally recognized as a bad practice to sniff user-agents. Instead, one should do their best to identify if a particular feature is in use or not. Depending on the feature, there are many ways one could accomplish that.

if hasattr(module_bar, 'py3_thing'):
    uniformly_named_foo = module_bar.py3_thing
else:
    uniformly_named_foo = module_bar.py2_thing

On my machine, the second route is twice as slow (not that an extra few hundred nanoseconds really matter for a one-time operation), but it doesn't seem to have any other major disadvantages. Are there advantages though? Are there Python installations where the second approach will be successful and the first will fail?


Solution

  • Yes. sys.version_info is a reliable way to determine Python version.

    See Python 3 documentation and Python 2 documentation.

    Note: sys.version_info is reliable, but not sys.version:

    sys.version

    A string containing the version number of the Python interpreter plus additional information on the build number and compiler used. This string is displayed when the interactive interpreter is started. Do not extract version information out of it, rather, use version_info and the functions provided by the platform module.

    If you're worried about bad modules changing the values of sys.version_info or something else, you can force a reload of <module 'sys' (built-in)>:

    import sys
    sys.version_info = "boo"
    print(sys.version_info)  # boo
    
    sys.modules.pop("sys")
    import sys  # reloaded
    print(sys.version_info)
    # Output: sys.version_info(major=3, minor=6, ...