The Python Tourist #5: Replacing sys.version_info with pyconfig
If you've spent any time writing Python code that is meant to be portable across multiple versions of Python, you've most likely written a few statements like this:
Using sys.version_info
# am I running on Python 2.2 and up?
if sys.version_info[0] >= 2 and sys.version_info[1] >= 2:
# do stuff for Python 2.2
else:
# do stuff for earlier versions
The problem I see with this is that it is ultra-verbose, and doesn't actually tell you what capability you require here. Although you can chop down the verbosity with a statement like:
Better, but ...
if sys.version_info[:2] >= (2,2):
...
That doesn't take care of the fact that you are relying on a hardcoded version number. There are a few downsides to this:
- If you came back to this code later, and wanted to improve the support for Python < 2.2, it isn't clear what capability is missing in Python < 2.2.
- With the creation of alternative Python implementations (like Jython) it isn't so clear what "2.2" means. Jython (currently) has a mix of 2.2 and 2.3 features, so a simple version check doesn't tell the whole story.
- If the format of version_info changes, your code breaks. It is better to work at a higher level of abstraction.
For the sake of argument, lets say that
IronPython has a different feature set than CPython and Jython. Picture writing code like this:
Check for implementation as well as version
if is_CPython():
if sys.version_info[:2] == (2,1):
# do 2.1 stuff
elif sys.version_info[:2] >= 2.2:
# do 2.2 stuff
elif is_Jython():
# do the Jython version checking ...
elif is_IronPython():
# and more version checking ...
After a while, it begins to feel like writing C-style
#ifdefs instead of Python.
The dynamic nature of Python means you can do all sort of neat introspective things, including introspection of runtime capabilities. Want to know if the current Python understands a particular piece of code? Run it and see!
There is a little module called
pyconfig that I wrote while working on
xml.pickle (part of
Gnosis_Utils). It is sort of like an
autoconf for Python, except it works at runtime. It is bundled with
Gnosis_Utils (since it uses it internally), but can be used as a stand-alone module, as there are no external dependencies.
The pyconfig module provides a set of prewritten tests to let you check for capabilities of the Python interpreter, instead of relying on version numbers.
Compare the following two code segments:
Without pyconfig
# need generator expressions
if sys.version_info[:2] >= (2,4):
# do something with generator expressions ...
# are True/False builtin?
if not (sys.version_info[:2] >= (2,2)):
# define my own True/False
# is 'enumerate()' available?
if sys.version_info[:2] >= (2,3):
# do something with enumerate()
Compare to the pyconfig-based code:
With pyconfig
from gnosis.pyconfig import pyconfig
# need generator expressions
if pyconfig.Have_GeneratorExpressions():
# do something with generator expressions ...
# are True/False builtin?
if not pyconfig.Have_TrueFalse():
# define my own True/False
# is 'enumerate()' available?
if pyconfig.Have_Enumerate():
# do something with enumerate()
In the second case, it is clear exactly what capability is needed. Also, the code is now robust across any nonstandard versions of Python that it might be running on.
If you import pyconfig as
from pyconfig import pyconfig, then all test results will be automatically cached. This allows you to use the tests inline with a minimum speed penalty.
pyconfig is written as set of small tests, with the reusable parts modularized. This makes it very easy to write any new tests you need. If nothing else, the source code to pyconfig is an interesting historical reference of the various PEPs that have been included over the evolution of Python.
Getting pyconfig
As mentioned, pyconfig comes bundled with
Gnosis_Utils:
Gnosis_UtilsOr if you prefer, you can grab it as a separate module:
pyconfig.py NOTE
The version bundled with Gnosis_Utils is the "stable" version. The standalone is the latest snapshot I've uploaded here, and may have newer features and/or bugs.