In my previous post, we developed a simple Python package that creates a file, somewhat similar to the
touch found in Linux systems.
$ pytouch new-file.txt $ ls [...] new-file.txt [...]
Explore the code from the previous post at tag
You can clone the code and follow along as we explore
tox and Travis CI in this post.
$ git clone https://github.com/p13i/pytouch.git & cd pytouch $ git checkout tags/v0.0.1 -b my-follow-along
Now, you’ll have the
pytouch code at
v0.0.1 ready to follow along on the new
Python packages don’t come packaged with an expressly- specified Python interpreter. So, our package will need to rely on our end users’ interpreter. As such, we need to ensure that our package will work on multiple different platforms without issue.
tox is a Python tool designed expressly for solving this problem. With
tox, we can easily specify multiple version of Python for which
tox then creates a virtual environment to run Python tests. Before exploring
tox, we’ll need to address some prerequisites and author the tests that
tox will run.
Before continuing, let’s create a
.gitignore file so that we don’t accidentally commit system-specific or auto-generated files. Please see the diff for new
.gitignore file’s contents.
In the previous post, I made a small typo that we can quickly fix. On line 6 of
pytouch/pytouch.py please change the
6 if len(sys.argv) != 2: ^
Authoring tests using
Next, we want to actually create the tests that will ensure our
main method in
pytouch/pytouch.py is working properly.
Create a new file called
tests.py in the root of the project. Fill in the file with this.
Assuming you are familiar with Python’s
unittest framework, let’s discuss a few lines in particular:
@mock.patch.object(sys, 'argv', ['pytouch'])
This method decorator alters the
sys.argvlist to be
['pytouch']for the scope of the succeeding method starting on
You may have noticed that the
import mock statement in
tests.py is not a standard, in-built Python module; it’s located on
PyPI and can be installed using
pip install mock. But how can we ensure that our package has access to this library?
We can specify it as a dependency in our
setup.py. Simply insert
install_requires=['mock'] in the
setup method of
setup.py and you should be good to go. See the completed
setup.py to double-check your work.
Now, we can finally run our tests!
$ python setup.py test running test [...] test_not_enough (tests.TestMainBadArgs) ... ok test_too_many (tests.TestMainBadArgs) ... ok test_too_many (tests.TestMainGoodArgs) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.001s OK
Now, we’re finally ready to test our module across multiple versions of the Python interpreter.
Create a file named
tox.ini (you could use our tool
pytouch tox.ini!). Add the following five lines to the file:
[tox] envlist = py26,py27,py33,py34,py35,py36 [testenv] commands = python setup.py test
envlist specifies the list of Python interpreters to run the
python setup.py test command in. Here,
py26 indicates a Python v2.6.x interpreter,
py27 indicates a Python v2.7.x interpreter, etc.
tox on your system or in a
virtualenv and run:
$ tox --skip-missing-interpreters
Because our host operating system may not contain all of six versions of Python specified in
envlist, we provide the
--skip-missing-interpreters flag so
tox won’t fail when it doesn’t find a particular interpreter.
At the end of a rather long console output, you’ll see
py26: commands succeeded py27: commands succeeded SKIPPED: py33: InterpreterNotFound: python3.3 SKIPPED: py34: InterpreterNotFound: python3.4 SKIPPED: py35: InterpreterNotFound: python3.5 py36: commands succeeded congratulations :)
indicating that our tool works for the specific Python interpreters available on the host system. In this case, you can see that my OS has Python 2.6, Python 2.7, and Python 3.6 installed. The lack of Python 3.3, 3.4, and 3.5 causes those iterations to be skipped by
But, you may note, we must test our tool on all versions of Python! We don’t know what our users will be using!
tox tests on multiple interpreters
We have a few options here:
- Install all six Python interpreters on one machine. (Sounds like a bad idea!)
- Use a container or virtual machine solution like Docker or Vagrant to manage an environment with multiple interpreters.
- Use a solution like Travis CI to run our tests in multiple environments on a managed cloud.
tox tests with Docker
If you’re familiar with Docker (or want to see how this option works), continue on. If not, please move on to the Travis CI section below.
I created a Docker image with all six of these interpreters installed so we can simply pull the ~500 MB image instead of installing each one by hand and risking all sorts of unintended consequences. First, pull the image:
docker pull p13i/docker-tox
Now, within our main
pytouch directory, we can simply run
$ docker run \ --rm \ --volume $(pwd):/app \ --workdir /app \ p13i/docker-tox \ tox
to run our
tox tests on all six specified interpreters.
Note that we didn’t pass in the
--skip-missing-interpreters flag because this Docker image comes with all six of these interpreters installed.
Running tox tests on Travis CI
Travis CI is a great, free tool for running tests for open-source projects.
Create a file named
language: python # These are all the different versions we # want to run our tests with python: - "2.6" - "2.7" - "3.3" - "3.4" - "3.5" - "3.6" # Install this package to easily run tox # in all these environments on Travis install: pip install tox-travis # The test script (as run locally) script: tox
Commit your changes and upload to your own git repository (or simply fork the existing one). After setting up your repository with Travis, you’ll see all our tests running on all six of these interpreters. From Travis, you can also get a status badge that will help our users know that our tool works properly:
Best of luck writing your own Python-based command line tools!
In my next post, I’ll show you how to upload Python packages to the official Python package index
© Pramod Kotipalli 2017
This site is open source. Improve this page »