An introduction to the very powerful Python tox tool

Introduction

tox is a generic virtualenv management and test command line tool

tox makes it easy to:

  • Test against different versions of Python (which would have alerted Kyle that the library hadn’t been tested against his install version).

  • Test against different dependency versions

  • Capture and run setup steps/ad hoc commands (which Kyle could have made a mistake on / not known about)

  • Isolate environment variables - By design, tox does not pass any evars from the system. Instead you are asked to explicitly declare them (which would have alerted Kyle to any environment variable requirements).

  • Do all the above across Windows / macOS / Linux (which would have saved Kyle if the issue had been due to the OS)

And tox will have done all of these things with a clean syntax which the team can lift and drop in their CI config to reduce the boilerplate there.

The tox documentation presents us with this diagram of the tox workflow:

python tox boilerplate

You can think of tox as a kind of combination of virtualenvwrapper and Makefile. Based on a config file (which we’ll look be looking at in the upcoming sections):

  • tox generates a series virtual environments

  • Installs dependencies for each environment (which are defined in config)

  • Runs setup commands (which are also defined in config) for each environment

  • Returns the results from each environment to the user.

You’ll find all tox’s hidden magic in the .tox directory that gets created as soon as you run any tox commands. So you could think of running tox as the equivalent of:

At the heart of tox is the config file.

As per the tox docs: “At the moment tox supports three configuration locations prioritized in the following order:

  • pyproject.toml

  • tox.ini

  • setup.cfg”

Here is our very basic tox.ini file:

[tox] envlist = my_env skipsdist = true [testenv] deps = pytest commands = pytest

The envlist which tells tox which environments to run when the command tox is entered to the command line. In our basic example here my_env is the name of the environment we will find in the .tox directory after running the tox command.

skipsdist which we need to set when we are not testing a Python package (e.g. for a service or simple set of scripts). Anytime tox doesn’t find a setup.py file this flag will need to be set. If you don’t set it you will see this error:

  • deps which are the dependencies required to run our tests - in this case simply pytest

  • commands which are the commands that will be triggered as part of the run for this environment. Because we specify pytest here, the pytest default behavior means that any Python files with “test” in the name will be passed to the test runner.

We’ve now added a setup.py file where we specify our numpy dependency. In our tox.ini file this means we remove the skipsdist=true line, and as a result when we run tox:

  • python setup.py sdist is run

  • The package (“squarer”) is installed in the tox virtualenv