How To: Correctly Test for Python's Version

Last updated:

Python has four obvious built-in ways to retrieve its current version. If your software has a minimum version requirement, you'll use one of these methods to test whether the Python currently running your code is a high-enough version to do so successfully.

Three of these ways are worthless, actively-harmful traps. If you read nothing else, read this: use sys.version_info.

The four ways are:

  • sys.version_info is the good way, returning a tuple of numbers, one for each component of the version
  • sys.version is a terrible way, returning a string containing the full version identifier
  • platform.python_version() is another terrible way, also returning a string
  • platform.python_version_tuple() is a final and especially terrible way, returning A TUPLE OF GODDAM STRINGS FOR THE COMPONENTS

Deets

sys.version_info returns a tuple of numeric components. Well, it returns a namedtuple, but same deal. If I convert it to a plain tuple, on my system I get:

(3, 7, 12, 'final', 0)

Tuples in Python have nice sorting behavior; they'll compare against other tuples element-wise, with missing items considered as coming before present items. This means that if I write sys.version_info >= (3, 7), it'll be true - the 3 and 7 match, then the remaining components in sys.version mean it's slightly greater than the (3, 7) literal.

Importantly, because this uses element-wise comparison and the elements are (mostly) numbers, it won't fail for stupid reasons, like version 3.10 coming out. If you compare (3, 10) >= (3, 7) it correctly returns True, because 10 is greater than 7.


platform.python_version_tuple() also returns a tuple of components, but each component is a string. Currently on my system it returns ('3', '7', '12').

If you compare it with a literal tuple, like platform.python_version_tuple() >= ('3', '7') (note that you have to write the literal with strings, or else it'll error), it'll appear to work - you'll get back True. If the function returns ('3', '6') or ('3', '8') it also appears to do the right thing, returning False and True respectively. Bump it to ('3', '9') and it's still True, as expected. But advance to ('3', '10') and it suddenly returns False, indicating that version 3.10 is less than version 3.7.

This is because string-wise comparison is not the same as numerical comparison. Strings compare letter-by-letter, and "1" (the first letter of "10") is indeed less than "7", and so fails the >= check.

This is a very easy mistake for end-users to make. It's an unforgiveable mistake for Python, the language, to make. Returning a tuple, indicating it's been parsed, but filling that tuple with strings when they know the components are numeric and will be used as if they were numbers, is just absolute clown-shoes behavior.


sys.version and platform.python_version() just straight up return strings. Once again, naive comparison seems to work: on my system sys.version returns '3.7.12 (default, Nov 11 2021, 11:50:43) \n[GCC 10.3.0]', and if you run sys.version >= '3.7', it returns True as expected.

Both of these fail when the version is 3.10, in the exact same way as platform.python_version_tuple().

Conclusion

Always, always, ALWAYS use sys.version_info to get the current Python version, for any purpose. Anything else will break your code for stupid reasons.

(a limited set of Markdown is supported)