- This chapter is part of the series:
- This tutorial builds upon the project structure from Chapter 3 and Chapter 4. I suggest understanding Chapter 3 and Chapter 4 before moving forward in order to understand the project structure.
- Chapter 3:
- Chapter 4:
-
So far, this is the structure of our code.
- src/ - something/ - __init__.py - py.typed - app.py - tests/ - __init__.py - conftest.py - test_app.py - .gitignore - LICENSE - pyproject.toml - README.md - requirements.txt - setup.cfg - setup.py -
Table of contents:
5.1 The Need for Tox
- The code so far has run on our local environment.
- How do we know that the code runs on multiple environments? Since we are building packages, we should know which versions of python can install our package and run it.
- This is where
Toxcomes in. To work withToxwe need atox.inifile which isTox's own configuration file. -
Let’s consider the following
tox.inifile in the root directory of the project.
[tox] minversion = 3.8.0 envlist = py36, py37, py38, py39, flake8, mypy isolated_build = true [gh-actions] python = 3.6: py36, mypy, flake8 3.7: py37 3.8: py38 3.9: py39 [testenv] setenv = PYTHONPATH = {toxinidir} deps = -r{toxinidir}/requirements_dev.txt commands = pytest --basetemp={envtmpdir} [testenv:flake8] basepython = python3.6 deps = flake8 commands = flake8 src tests [testenv:mypy] basepython = python3.6 deps = -r{toxinidir}/requirements_dev.txt commands = mypy src To read more about
toxfile structure, we can read: https://tox.wiki/en/latest/config.html.
5.2 Understanding the Tox environments and variables
- We can read about all of this from
[tox.wiki](http://tox.wiki), https://tox.wiki/en/latest/config.html. -
All global settings are defined in the
[tox]section. In our case,
[tox] minversion = 3.8.0 envlist = py36, py37, py38, py39, flake8, mypy isolated_build = true-
minversionis the minimum version oftox-libraryrequired to parse thistoxfile.-
3.8.0is the minimum version oftox-libraryrequired to parse thistoxfile.
-
-
envlistis for determining the environment list thattoxis to operate on and so on. - There are many other global setting variables that are available in the
tox.wikiwebsite. We do not need to understand everything as of now.
-
-
Test environments are defined under the
testenvsection and individualtestenv:NAMEsections, whereNAMEis the name of a specific environment. This section also has a lot of options to set. For our case, we will go through:-
basepython: Name or path to a Python interpreter which will be used for creating the virtual environment. -
deps: Environment dependencies. Installed usually from requirements file or manually written. -
commands: The commands to be called for testing. Only execute if[commands_pre](https://tox.wiki/en/latest/config.html#conf-commands_pre)succeeds.commands_preis another option we have not talked about. -
setenv: Each line contains a NAME=VALUE environment variable setting which will be used for all test command invocations.
-
-
We might also notice
{toxinidir}which is a variable inbuilt intox. We can learning more about changing its working directory: https://stackoverflow.com/questions/52503796/change-tox-workdir.- For now, we just need to understand that: from
tox global settingssection fromtoxdocumentation, the.toxdirectory which is working dir, is created in directory wheretox.iniis located. - Turns out
toxalso creates a working directory just likegitdoes.
- For now, we just need to understand that: from
-
We also have
{envtmpdir}which is another variable inbuilt intox.-
{envdir}basically points to thevirtualenvdirectory thattoxcreates. - If we do
{envdir}/tmpit points to thetmpdirectory inside thevirtualenv. - This is what
{envtmpdir}points to. Basically{envtmpdir} = {envdir}/tmp. - It will be cleared each time before the group of test commands is invoked. In our case, we have three group of test commands: a
[test]which is inbuilt intoxand twotest:NAMEwhich are our custom test groups. - There are other similar variables:
{envlogdir}={envdir/log}. It defines a directory for logging wheretoxwill put logs oftool invocation.
-
tox-gh-actionsis atoxplugin which helps runningtoxonGitHub Actionswith multiple differentPython versionson multiple workers in parallel.
5.3 Understanding the flow of Tox environments
-
Toxallows us to create a bunch of differentvirtual environments,installourpackageinto thoseenvironmentsand then run thetest groupson each of thoseenvironments. -
The first four
py36, py37, py38, py39,arebuilt-inversions ofpythonthattoxalready knows about.
[tox] minversion = 3.8.0 envlist = py36, py37, py38, py39, flake8, mypy isolated_build = true -
Their configuration goes in the
testenvblock and the tests are run in those environments with the following commands.
[testenv] setenv = PYTHONPATH = {toxinidir} deps = -r{toxinidir}/requirements_dev.txt commands = pytest --basetemp={envtmpdir}- We set the
PYTHONPATHto{toxinidir}. From the previous section we know it equals toPYTHONPATH=<top level of project directory>. Since, we knowtoxinidirpoints to wherever thetox.inifile is located. In our case, it is located at the top level of the project directory. - As you can see, in
testenv, we have setdeps=-r{toxinidir}/requirements_dev.txt. From the previous section we know it equals todeps=-r <project_directory>/requirements_dev.txt. Since, we knowtoxinidirpoints to wherever thetox.inifile is located. In our case, it is located at the top level of the project directory. - We install the requirements and then run the
pytest --basetemp=<virtualenv>/tmpcommand in all of the environments passed along. Since, we know that{envtmpdir}points to<virtualenv>/tmpdirectory.
- We set the
-
Then we have the last two environments:
flake8,mypy.
[tox] minversion = 3.8.0 envlist = py36, py37, py38, py39, flake8, mypy isolated_build = true -
flake8andmypyare notbuilt-inenvironments intox. Therefore, we have to create separatetestenv:NAMEblock for them. Which is why we added:
[testenv:flake8] basepython = python3.6 deps = flake8 commands = flake8 src tests [testenv:mypy] basepython = python3.6 deps = -r{toxinidir}/requirements_dev.txt commands = mypy src Here,
testenv:flake8is theflake8virtual environment andtestenv:mypyis themypyvirtual environment.Since
flake8andmypyare not versions of python and only commands that we want to run, we need to specify which version of python we want to run on. We do this onbasepython.depsagain are all the dependencies. We can specify individual dependencies or we can specify the requirements file.commandsis the actual command that runs in these blocks.We also have,
gh-actionsblock which we will discuss in the next chapter.
5.4 Executing TOX
- Once we have the
tox.inifile and thetoxcommand. - To get the
toxcommand,pip install tox. -
We can now just run the
toxcommand and everything will be automated.
$ tox -
When we run it, we notice that it takes very long. Why is that?
- When we run the tests locally, it runs on our virtual environment.
- But like we discussed,
toxcreates a new virtual environment, install everything into that and run tests in all of those environments.
It is advised to run
toxonly before commit and push. Otherwise, just usepytest, to save time during development.
Top comments (0)