@@ -66,7 +66,6 @@ This is a typical layout for many projects. Let’s take a look at a simple
6666
6767 [tox]
6868 envlist = py313
69- isolated_build = True
7069
7170 [testenv]
7271 deps =
@@ -79,11 +78,6 @@ shortcut that tells tox to run our tests with Python version 3.13. We will be
7978adding more Python versions shortly, but using one version helps to understand
8079the flow of tox.
8180
82- Also note the line ``isolated_build = True ``: This is required for all packages
83- configured with :file: `pyproject.toml `. However, for all projects configured
84- with :file: `setup.py ` that use the :term: `setuptools ` library, this line can be
85- omitted.
86-
8781In the ``[testenv] `` section, ``pytest `` and ``faker `` are listed as
8882dependencies under ``deps ``. So tox knows that we need these two tools for
8983testing. If you wish, you can also specify which version should be used, for
@@ -112,15 +106,20 @@ To run tox, simply start tox:
112106.. code-block :: pytest
113107
114108 $ uv run tox
115- py313: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/57/items-0.1.0.tar.gz
109+ .pkg: _optional_hooks> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build
110+ .pkg: get_requires_for_build_sdist> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build
111+ .pkg: build_sdist> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build
112+ py313: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/18/items-0.1.0.tar.gz
116113 py313: commands[0]> python --version --version
114+ Python 3.13.0 (main, Oct 7 2024, 23:47:22) [Clang 18.1.8 ]
115+ py313: commands[1]> coverage run -m pytest
117116 ============================= test session starts ==============================
118- platform darwin -- Python 3.13.0, pytest-8.4.1 , pluggy-1.6.0
117+ platform darwin -- Python 3.13.0, pytest-9.0.2 , pluggy-1.6.0
119118 cachedir: .tox/py313/.pytest_cache
120119 rootdir: /Users/veit/cusy/prj/items
121120 configfile: pyproject.toml
122121 testpaths: tests
123- plugins: anyio-4.9.0, Faker-37.4 .0, cov-6.2.1
122+ plugins: Faker-40.1 .0, cov-7.0.0
124123 collected 83 items
125124
126125 tests/api/test_add.py ...... [ 7%]
@@ -149,8 +148,10 @@ To run tox, simply start tox:
149148 tests/cli/test_update.py . [ 98%]
150149 tests/cli/test_version.py . [100%]
151150
152- ============================== 83 passed in 0.27s ==============================
153- py313: OK ✔ in 1.17 seconds
151+ ============================== 83 passed in 0.35s ==============================
152+ .pkg: _exit> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build
153+ py313: OK (1.19=setup[0.45]+cmd[0.01,0.72] seconds)
154+ congratulations :) (1.23 seconds)
154155
155156 Testing multiple Python versions
156157--------------------------------
@@ -159,58 +160,98 @@ To do this, we extend ``envlist`` in the :file:`tox.ini` file to add further
159160Python versions:
160161
161162.. code-block :: ini
162- :emphasize-lines: 2, 4
163163
164164 [tox]
165- envlist = py3{9,10,11,12,13,13t,14,14t}
166- isolated_build = True
165+ envlist =
166+ py3{10-14}
167+ py3{13-14}t
167168 skip_missing_interpreters = True
168169
169- We will now test Python versions from 3.8 to 3.11 . In addition, we have also
170+ We will now test Python versions from 3.10 to 3.14 . In addition, we have also
170171added the setting ``skip_missing_interpreters = True `` so that tox does not fail
171172if one of the listed Python versions is missing on your system. If the value is
172173set to ``True ``, tox will run the tests with every available Python version, but
173174will skip versions it doesn’t find without failing. The output is very similar,
174175although I will only highlight the differences in the following illustration:
175176
176177.. code-block :: pytest
177- :emphasize-lines: 3-4, 8-12, 16-20, 24-28, 32-
178-
179- $ uv run tox
180- ...
181- py39: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/17/items-0.1.0.tar.gz
182- py39: commands[0]> coverage run -m pytest
183- ============================= test session starts ==============================
184- ...
185- ============================== 49 passed in 0.16s ==============================
186- py39: OK ✔ in 2.17 seconds
187- py310: skipped because could not find python interpreter with spec(s): py310
188- py310: SKIP ⚠ in 0.01 seconds
189- py311: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/18/items-0.1.0.tar.gz
190- py311: commands[0]> coverage run -m pytest
191- ============================= test session starts ==============================
192- ...
193- ============================== 49 passed in 0.15s ==============================
194- py311: OK ✔ in 1.41 seconds
195- py312: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/19/items-0.1.0.tar.gz
196- py312: commands[0]> coverage run -m pytest
197- ============================= test session starts ==============================
198- ...
199- ============================== 49 passed in 0.15s ==============================
200- py312: OK ✔ in 1.43 seconds
201- py313: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/20/items-0.1.0.tar.gz
202- py313: commands[0]> coverage run -m pytest
203- ============================= test session starts ==============================
204- ...
205- ============================== 49 passed in 0.16s ==============================
206- .pkg: _exit> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build
207- py313: OK ✔ in 1.48 seconds
208- py39: OK (2.17=setup[1.54]+cmd[0.63] seconds)
209- py310: SKIP (0.01 seconds)
210- py311: OK (1.41=setup[0.81]+cmd[0.60] seconds)
211- py312: OK (1.43=setup[0.82]+cmd[0.61] seconds)
212- py313: OK (1.48=setup[0.82]+cmd[0.66] seconds)
213- congratulations :) (10.46 seconds)
178+ :emphasize-lines: 3-6, 10-14, 18-22, 26-30, 34-38, 42-46, 50-54, 59-
179+
180+ $ uv run tox
181+ ...
182+ py310: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/19/items-0.1.0.tar.gz
183+ py310: commands[0]> python --version --version
184+ Python 3.10.17 (main, Apr 9 2025, 03:47:39) [Clang 20.1.0 ]
185+ py310: commands[1]> coverage run -m pytest
186+ ============================= test session starts ==============================
187+ ...
188+ ============================== 83 passed in 0.35s ==============================
189+ py310: OK ✔ in 1.3 seconds
190+ py311: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/20/items-0.1.0.tar.gz
191+ py311: commands[0]> python --version --version
192+ Python 3.11.11 (main, Feb 5 2025, 18:58:27) [Clang 19.1.6 ]
193+ py311: commands[1]> coverage run -m pytest
194+ ============================= test session starts ==============================
195+ ...
196+ ============================== 83 passed in 0.36s ==============================
197+ py311: OK ✔ in 1.16 seconds
198+ py312: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/21/items-0.1.0.tar.gz
199+ py312: commands[0]> python --version --version
200+ Python 3.12.12 (main, Oct 14 2025, 21:38:21) [Clang 20.1.4 ]
201+ py312: commands[1]> coverage run -m pytest
202+ ============================= test session starts ==============================
203+ ...
204+ ============================== 83 passed in 0.55s ==============================
205+ py312: OK ✔ in 1.79 seconds
206+ py313: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/22/items-0.1.0.tar.gz
207+ py313: commands[0]> python --version --version
208+ Python 3.13.0 (main, Oct 7 2024, 23:47:22) [Clang 18.1.8 ]
209+ py313: commands[1]> coverage run -m pytest
210+ ============================= test session starts ==============================
211+ ...
212+ ============================== 83 passed in 0.35s ==============================
213+ py313: OK ✔ in 1.07 seconds
214+ py314: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/23/items-0.1.0.tar.gz
215+ py314: commands[0]> python --version --version
216+ Python 3.14.0 (main, Oct 14 2025, 21:10:22) [Clang 20.1.4 ]
217+ py314: commands[1]> coverage run -m pytest
218+ ============================= test session starts ==============================
219+ ...
220+ ============================== 83 passed in 0.36s ==============================
221+ py314: OK ✔ in 1.28 seconds
222+ py313t: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/24/items-0.1.0.tar.gz
223+ py313t: commands[0]> python --version --version
224+ Python 3.13.0 experimental free-threading build (main, Oct 16 2024, 08:24:33) [Clang 18.1.8 ]
225+ py313t: commands[1]> coverage run -m pytest
226+ ============================= test session starts ==============================
227+ ...
228+ ============================== 83 passed in 0.49s ==============================
229+ py313t: OK ✔ in 1.51 seconds
230+ py314t: install_package> .venv/bin/uv pip install --reinstall --no-deps items@/Users/veit/cusy/prj/items/.tox/.tmp/package/25/items-0.1.0.tar.gz
231+ py314t: commands[0]> python --version --version
232+ Python 3.14.0b4 free-threading build (main, Jul 8 2025, 21:06:49) [Clang 20.1.4 ]
233+ py314t: commands[1]> coverage run -m pytest
234+ ============================= test session starts ==============================
235+ ...
236+ ============================== 83 passed in 0.39s ==============================
237+ .pkg: _exit> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build
238+ py310: OK (1.30=setup[0.54]+cmd[0.01,0.75] seconds)
239+ py311: OK (1.16=setup[0.38]+cmd[0.01,0.76] seconds)
240+ py312: OK (1.79=setup[0.42]+cmd[0.01,1.36] seconds)
241+ py313: OK (1.07=setup[0.34]+cmd[0.01,0.71] seconds)
242+ py314: OK (1.28=setup[0.42]+cmd[0.01,0.85] seconds)
243+ py313t: OK (1.51=setup[0.44]+cmd[0.01,1.05] seconds)
244+ py314t: OK (1.34=setup[0.44]+cmd[0.01,0.89] seconds)
245+ congratulations :) (9.48 seconds)
246+
247+ .. versionchanged :: tox≥4.25.0
248+ Before tox 4.25.0 dated 27 March 2025, the versions had to be specified one
249+ by one:
250+
251+ .. code-block :: ini
252+
253+ [tox]
254+ envlist = py3{10,11,12,13,14,13t,14t}
214255
215256 Running Tox environments in parallel
216257------------------------------------
@@ -221,17 +262,20 @@ other. It is also possible to run them in parallel with the ``-p`` option:
221262.. code-block :: pytest
222263
223264 $ uv run tox -p
224- py310: SKIP ⚠ in 0.09 seconds
225- py312: OK ✔ in 2.08 seconds
226- py313: OK ✔ in 2.18 seconds
227- py311: OK ✔ in 2.23 seconds
228- py39: OK ✔ in 2.91 seconds
229- py39: OK (2.91=setup[2.17]+cmd[0.74] seconds)
230- py310: SKIP (0.09 seconds)
231- py311: OK (2.23=setup[1.27]+cmd[0.96] seconds)
232- py312: OK (2.08=setup[1.22]+cmd[0.86] seconds)
233- py313: OK (2.18=setup[1.23]+cmd[0.95] seconds)
234- congratulations :) (3.05 seconds)
265+ py311: OK ✔ in 1.7 seconds
266+ py310: OK ✔ in 1.8 seconds
267+ py313: OK ✔ in 1.8 seconds
268+ py314t: OK ✔ in 1.89 seconds
269+ py314: OK ✔ in 1.91 seconds
270+ py313t: OK ✔ in 2.24 seconds
271+ py310: OK (1.80=setup[0.62]+cmd[0.02,1.16] seconds)
272+ py311: OK (1.70=setup[0.54]+cmd[0.02,1.15] seconds)
273+ py312: OK (2.28=setup[0.58]+cmd[0.01,1.69] seconds)
274+ py313: OK (1.80=setup[0.60]+cmd[0.02,1.18] seconds)
275+ py314: OK (1.91=setup[0.62]+cmd[0.02,1.28] seconds)
276+ py313t: OK (2.24=setup[0.72]+cmd[0.02,1.51] seconds)
277+ py314t: OK (1.89=setup[0.61]+cmd[0.02,1.26] seconds)
278+ congratulations :) (2.33 seconds)
235279
236280 .. note ::
237281 The output is not abbreviated; this is the full output you will see if
@@ -247,11 +291,12 @@ the ``pytest-cov`` plugin is installed in the tox test environments. Including
247291extend commands to ``pytest --cov=items ``:
248292
249293.. code-block ::
250- :emphasize-lines: 12 -
294+ :emphasize-lines: 11 -
251295
252296 [tox]
253- envlist = py3{9,10,11,12,13,13t,14,14t}
254- isolated_build = True
297+ envlist =
298+ py3{10-14}
299+ py3{13-14}t
255300 skip_missing_interpreters = True
256301
257302 [testenv]
@@ -273,7 +318,7 @@ When using Coverage with ``tox``, it can sometimes be useful to add a section in
273318the :file: `pyproject.toml ` file to tell Coverage which source code paths should
274319be considered identical:
275320
276- .. code-block :: ini
321+ .. code-block :: toml
277322
278323 [tool.coverage.paths]
279324 source = ["src", ".tox/py*/**/site-packages"]
@@ -288,23 +333,20 @@ example.
288333
289334 $ uv run tox
290335 ...
291- coverage-report: commands[0]> coverage combine
292- Combined data file .coverage.fay.local.19539.XpQXpsGx
293- coverage-report: commands[1]> coverage report
294- Name Stmts Miss Branch BrPart Cover Missing
295- --------------------------------------------------------------
296- src/items/api.py 68 1 12 1 98% 88
297- --------------------------------------------------------------
298- TOTAL 428 1 32 1 99%
299-
300- 26 files skipped due to complete coverage.
301- py39: OK (2.12=setup[1.49]+cmd[0.63] seconds)
302- py310: SKIP (0.01 seconds)
303- py311: OK (1.41=setup[0.80]+cmd[0.62] seconds)
304- py312: OK (1.43=setup[0.81]+cmd[0.62] seconds)
305- py313: OK (1.46=setup[0.83]+cmd[0.62] seconds)
306- coverage-report: OK (0.16=setup[0.00]+cmd[0.07,0.09] seconds)
307- congratulations :) (10.26 seconds)
336+ Name Stmts Miss Branch BrPart Cover Missing
337+ ---------------------------------------------------
338+ TOTAL 540 0 32 0 100%
339+
340+ 33 files skipped due to complete coverage.
341+ py310: OK (1.10=setup[0.44]+cmd[0.01,0.64] seconds)
342+ py311: OK (0.98=setup[0.31]+cmd[0.01,0.66] seconds)
343+ py312: OK (1.59=setup[0.34]+cmd[0.01,1.24] seconds)
344+ py313: OK (1.06=setup[0.34]+cmd[0.01,0.71] seconds)
345+ py314: OK (1.10=setup[0.35]+cmd[0.01,0.74] seconds)
346+ py313t: OK (1.36=setup[0.40]+cmd[0.01,0.95] seconds)
347+ py314t: OK (1.31=setup[0.44]+cmd[0.01,0.86] seconds)
348+ coverage-report: OK (1.55=setup[0.37]+cmd[1.08,0.10] seconds)
349+ congratulations :) (10.09 seconds)
308350
309351 Set minimum coverage
310352--------------------
@@ -336,15 +378,15 @@ We can also call individual tests with tox by making another change so that
336378:term: `parameters <Parameter> ` can be passed to pytest:
337379
338380.. code-block :: ini
339- :emphasize-lines: 17
381+ :emphasize-lines: 15-
340382
341383 [tox]
342384 envlist =
343385 pre-commit
344386 docs
345- py3{9,10,11,12,13,13t,14,14t}
387+ py3{10-14}
388+ py3{13-14}t
346389 coverage-report
347- isolated_build = True
348390 skip_missing_interpreters = True
349391
350392 [testenv]
0 commit comments