In this article, we will go over how we can use a makefile and tox to automate various Python-related, command-line (CLI) tools. This article assumes you are running bash or an equivalent.
tox is an automation tool that is primarily used to add in testing. On the tox website, it describes itself as:
tox aims to automate and standardize testing in Python. It is part of a larger vision of easing the packaging, testing, and release process of Python software.
You can define a configuration file tox.ini
– this is where you will define all of your tox environments. In the example below, we have two environments, testenv
to run our tests and testenv:lint
to
[tox]
envlist = py36,py37,lint
[testenv]
basepython =
{lint}: {env:TOXPYTHON:python3}
py36: {env:TOXPYTHON:python3.6}
py37: {env:TOXPYTHON:python3.7}
passenv = *
install_command = pip install {opts} {packages}
deps =
pytest
pytest-mock
usedevelop = false
commands = pytest -v {posargs} tests
[testenv:lint]
skip_install = true
deps = flake8
commands = flake8 src/
tox creates a virtual environment (virtualenv) for each tox environment defined in the configuration file (tox.ini
). It then runs our command within that virtualenv, you can see these if you take a look in the .tox
folder. So, in our lint example, it would create a virtualenv called lint in the .tox folder, install our dependencies (flake8
), and finally run the command flake8 src/
(within the lint virtualenv). You can read more about how tox works over here. So, how do we run a Tox environment? Well,like so:
# Install Tox
pip install tox
# Run the tox environment
tox -e lint
We can pass extra parameters to tox environments using the {posargs}
. So, for example, if we had an environment defined as:
[testenv:bumpversion]
skip_install = true
deps = bumpversion
commands = bumpversion --verbose {posargs}
We could run it like so, tox -e bumpversion -- --allow-dirty patch
(note the extra --
).
As you can see, tox allows us to automate tedious Python-related tasks like code formatting, running the lint, and running unit tests. We can test our code against different versions of Python as well, such as Python3.6 or Python3.7, to make sure our code is compatible with both.
If we wanted to run pytest against python3.6 we could do it like so, tox -e py36
. If we want to run pytest against python3.7 we could do it with, tox -e py37
(given the same configuration file as above). Some common tools used in conjunction with tox are:
Makefiles are often used in C/C++ programs to compile the code/generate binaries, etc., and to automate (often long-winded) tasks. To use a make file, all you need to do is create a file called Makefile
. Each “job” in the makefile is called a target
; for example, a makefile may look like:
PY = py36
# prompt_example> make test PY=py36 OPTIONS="-- -s"
.PHONY: test
test:
@tox -e $(PY) $(OPTIONS)
.PHONY: lint
lint:
@tox -e lint
So, now, if we want to run our linter, we could do make lint
– to run our tests, we can type the command make test
. If we want to specify a Python version, we could do make test PY=py37
(note how $(PY)
is a variable we can override). This may remind you of tools available to other languages such as package.json
for JavaScript/NodeJS. The main advantage of using a Makefile with tox is that we can define targets in our makefile that aren’t specifically related to Python tools, such as cleaning our project:
.PHONY: clean
clean:
@find . -type f -name '*.pyc' -delete
@find . -type d -name '__pycache__' | xargs rm -rf
@find . -type d -name '*.ropeproject' | xargs rm -rf
@rm -rf build/
@rm -rf dist/
@rm -f src/*.egg*
@rm -f MANIFEST
@rm -rf docs/build/
@rm -f .coverage.*
That’s it! A simple introduction on how you can use a Makefile
and tox
to automate various, tedious tasks.
Free Resources