I use Python’s
tox to orchestrate a lot of my tests. It lets you set a list of versions in a
tox.ini file (in the same directory as your
setup.py), like this:
[tox] envlist = py37, py38 [testenv] allowlist_externals = echo commands = echo "success"
Then you can run the
tox command, it’ll create a
venv for each version, and run your tests in each of those environments. It’s an easy way to ensure your code works across all the versions of Python you want to support.
But, if I install
tox into a 3.8 environment and run the
tox command in the directory where we created the
tox.ini above, I get this:
tox GLOB sdist-make: /Users/adam/Local/fiddle/setup.py py37 create: /Users/adam/Local/fiddle/.tox/py37 ERROR: InterpreterNotFound: python3.7 py38 create: /Users/adam/Local/fiddle/.tox/py38 py38 inst: /Users/adam/Local/fiddle/.tox/.tmp/package/1/example-0.0.0.zip py38 installed: example @ file:///Users/adam/Local/fiddle/.tox/.tmp/package/1/example-0.0.0.zip py38 run-test-pre: PYTHONHASHSEED='2325607949' py38 run-test: commands | echo success success ___________________________________________________________________________ summary ____________________________________________________________________________ ERROR: py37: InterpreterNotFound: python3.7 py38: commands succeeded
It found the 3.8 interpreter I ran it with, but it couldn’t find 3.7.
pyenv can get you past this. It’s a utility for installing and switching between multiple Python versions. I use it on OS X (⬅️ instructions to get set up, if you’re not already). Here’s how it looks when I have Python 3.6, 3.7, and 3.8 installed, and I’m using 3.8:
pyenv versions system 3.6.11 3.7.9 * 3.8.5 (set by /Users/adam/.pyenv/version)
Just having those versions installed isn’t enough, though. You still get the error from
tox about missing versions. You have to specifically enable each version:
pyenv local 3.8.5 3.7.9 pyenv versions system 3.6.11 * 3.7.9 (set by /Users/adam/Local/fiddle/.python-version) * 3.8.5 (set by /Users/adam/Local/fiddle/.python-version)
This will create a
.python-version file in the current directory that sets your Python versions.
pyenv will read that file whenever you’re in that directory. You can also set versions that’ll be picked up in any folder with the
pyenv global command.
tox will pick up both versions:
tox GLOB sdist-make: /Users/adam/Local/fiddle/setup.py py37 inst-nodeps: /Users/adam/Local/fiddle/.tox/.tmp/package/1/example-0.0.0.zip py37 installed: example @ file:///Users/adam/Local/fiddle/.tox/.tmp/package/1/example-0.0.0.zip py37 run-test-pre: PYTHONHASHSEED='1664367937' py37 run-test: commands | echo success success py38 inst-nodeps: /Users/adam/Local/fiddle/.tox/.tmp/package/1/example-0.0.0.zip py38 installed: example @ file:///Users/adam/Local/fiddle/.tox/.tmp/package/1/example-0.0.0.zip py38 run-test-pre: PYTHONHASHSEED='1664367937' py38 run-test: commands | echo success success ___________________________________________________________________________ summary ____________________________________________________________________________ py37: commands succeeded py38: commands succeeded congratulations :)
That’s it! Now you can run your tests in as many verions of Python as you need.
Need more than just this article? We’re available to consult.
You might also want to check out these related articles: