by Bernat Gabor /
@gjbernat /
Bloomberg
https://bit.ly/tox-py-us-21
Photo by Bernat Gabor - All rights reserved
virtualenv venv
pip install .
# or the longer form
# pyproject-build --sdist .
# .\venv\Scripts\pip install .\dist\pi_approx-1.0.0.tar.gz
.\venv\Scripts\pip.exe install 'pytest>=6'
.\venv\Scripts\pytest tests
[tox]
isolated_build = true
[testenv]
deps = pytest>=6
commands = pytest tests
tox -e py39
# bonus do this for any python version: tox -e py310,py38
today 1992 commits - 4 million downloads per month - 14k lines
first Sat Jun 5 11:45:30 2010 +0200 - released
12th July 2010
β― tox --version
3.23.1 imported from c:\users\gabor\.local\pipx\venvs\tox\lib\site-packages\tox\__init__.py
soon 208+ commits - complete rewrite - 12.5k lines
Sun Mar 29 15:35:05 2020 +0100 - released
Q3/Q4 2021
β― tox --version
4.0.0 from c:\users\gabor\.local\pipx\venvs\tox\lib\site-packages\tox\__init__.py
Remove technical debts (drop Python 2 + add type hints)
class TestenvConfig:
# Python 3 only, as __getattribute__ is ignored for old-style types on Python 2
def __getattribute__(self, name):
rv = object.__getattribute__(self, name)
if isinstance(rv, Exception):
raise rv
return rv
if six.PY2:
def __getattr__(self, name):
if name in self._missing_subs:
raise self._missing_subs[name]
raise AttributeError(name)
def get_envbindir(self):
...
Remove technical debts (drop Python 2 + add type hints)
from typing import Callable, Dict, Iterator, List, Mapping, Optional, Tuple
Replacer = Callable[[str, List[str]], str]
class SetEnv:
def __init__(self, raw: str) -> None:
self.replacer: Replacer = lambda s, c: s
self._later: List[str] = []
self._raw: Dict[str, str] = {}
...
def load(self, item: str, chain: Optional[List[str]] = None) -> str:
if chain is None:
chain = [f"env:{item}"]
if item in self._materialized:
return self._materialized[item]
raw = self._raw[item]
result = self.replacer(raw, chain) # apply any replace options
self._materialized[item] = result
self._raw.pop(item, None) # if the replace requires the env we may be called again, so allow pop to fail
return result
def __contains__(self, item: object) -> bool:
return isinstance(item, str) and item in self.__iter__()
...
Better abstractions
β― lsd --tree .\src\tox\config
ο config
βββ ξ __init__.py
βββ ξ parallel.py
βββ ξ reporter.py
β― lsd --tree .\src\tox\config
ο config
βββ ξ __init__.py
βββ ο cli
β βββ ξ __init__.py
β βββ ξ env_var.py
β βββ ξ ini.py
β βββ ξ parse.py
β βββ ξ parser.py
βββ ο loader
β βββ ξ __init__.py
β βββ ξ api.py
β βββ ξ convert.py
β βββ ο ini
β β βββ ξ __init__.py
β β βββ ξ factor.py
β β βββ ξ replace.py
β βββ ξ memory.py
β βββ ξ str_convert.py
β βββ ξ stringify.py
βββ ξ main.py
βββ ξ of_type.py
βββ ξ set_env.py
βββ ξ sets.py
βββ ο source
β βββ ξ __init__.py
β βββ ξ api.py
β βββ ξ discover.py
β βββ ξ ini.py
β βββ ξ legacy_toml.py
β βββ ξ setup_cfg.py
β βββ ξ tox_ini.py
βββ ξ types.py
Improve performance
β― hyperfine 'tox4 -a' 'tox -a'
Benchmark #1: tox4 -a
Time (mean Β± Ο): 408.2 ms Β± 24.6 ms [User: 0.0 ms, System: 10.0 ms]
Range (min β¦ max): 369.7 ms β¦ 460.0 ms 10 runs
Benchmark #2: tox -a
Time (mean Β± Ο): 1.240 s Β± 0.037 s [User: 2.8 ms, System: 7.3 ms]
0 Range (min β¦ max): 1.205 s β¦ 1.334 s 10 runs
Summary
'tox4 -a' ran
3.04 Β± 0.20 times faster than 'tox -a'
More colors
More colors
Wheels support (not just sdist)
[testenv]
package = wheel
isolated_build_env = .pkg
Introduce new interfaces -> Future new features
β― tox --runner conda
β― tox --runner docker --image python:3.9
β― tox --runner docker --image ubuntu:latest
β― tox -e package_js
Better UI - separation of concerns
show default tox targets
β― tox -l
py39
py38
show all tox targets
β― tox -a
py39
py38
extra
show configuration values for a tox target
β― tox --showconfig -e py39
[testenv:py39]
envdir = C:\Users\gabor\git\tox-ini-fmt\.tox\py39
setenv = SetenvDict: {'COVERAGE_FILE': '{toxworkdir}
...
β― tox -a -l --showconfig -e py39
???
sub-commands to the rescue
β― tox4 --help
run (r) run environments
run-parallel (p) run environments in parallel
depends (de) visualize tox environment dependencies
list (l) list environments
devenv (d) sets up a development environment at ENVDIR based on the tox configuration specified
config (c) show tox configuration
quickstart (q) Command line script to quickly create a tox config file for a Python project
legacy (le) legacy entry-point command
# config file: 'C:\\Users\\gabor\\AppData\\Local\\tox\\tox\\config.ini' missing (change via env var TOX_CONFIG_FILE)
# [tox]
# color = 0
tox4 r -e test
#
#
#
env TOX_COLOR=0 tox4 r -e test
#
#
#
env TOX_COLOR=0 tox4 r -e test -x testenv.base_python=python3.9
Help us now so we don't have to test in PROD (your CI environments)
Try our pre-releases
β― virtualenv venv
β― venv/bin/pip install tox --pre
β― venv/bin/tox4
PS. we broke all the plugins; if you maintain a plugin, please reach out to us