@@ -21,26 +21,23 @@ create an installable :doc:`distribution of your package
2121<../packs/distribution>`. It searches the :file: `tox.ini ` file for a list of
2222environments and then performs the following steps for each:
2323
24- #. creates a :term: `virtual environment <Virtual environment> `,
25- #. installs some dependencies with :term: `pip `,
26- #. build your package,
27- #. install your package with pip,
28- #. run further tests.
24+ #. creates a :term: `virtual environment <Virtual environment> `
25+ #. installs some dependencies with :term: `pip `
26+ #. build your package
27+ #. install your package with pip
28+ #. run further tests
2929
3030After all environments have been tested, tox outputs a summary of the results.
3131
32- .. note ::
33- Although tox is used by many projects, there are alternatives that fulfil
34- similar functions. Two alternatives to tox are `nox
35- <https://nox.thea.codes/en/stable/> `_ and `invoke
36- <https://www.pyinvoke.org> `_.
32+ To accelerate this process with :term: `uv `, we don’t use tox directly, but
33+ `tox-uv <https://github.com/tox-dev/tox-uv >`_.
3734
3835Setting up tox
3936--------------
4037
4138Until now, we had the items code in a :file: `src/ ` directory and the tests in
42- :file: `tests/api/ ` and :file: `tests/cli/ `. Now we will add a :file: `tox.ini ` file
43- so that the structure looks like this:
39+ :file: `tests/api/ ` and :file: `tests/cli/ `. Now we will add a :file: `tox.ini `
40+ file so that the structure looks like this:
4441
4542.. code-block :: console
4643 :emphasize-lines: 16
@@ -83,77 +80,77 @@ adding more Python versions shortly, but using one version helps to understand
8380the flow of tox.
8481
8582Also note the line ``isolated_build = True ``: This is required for all packages
86- configured with :file: `pyproject.toml `. However, for all projects configured with
87- :file: `setup.py ` that use the :term: `setuptools ` library, this line can be
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
8885omitted.
8986
90- In the ``[testenv] `` section, ``pytest `` and ``faker `` are listed as dependencies
91- under ``deps ``. So tox knows that we need these two tools for testing. If you
92- wish, you can also specify which version should be used, for example
93- ``pytest>=6.0 ``. Finally, commands instruct tox to execute ``pytest `` in every
94- environment.
87+ In the ``[testenv] `` section, ``pytest `` and ``faker `` are listed as
88+ dependencies under ``deps ``. So tox knows that we need these two tools for
89+ testing. If you wish, you can also specify which version should be used, for
90+ example ``pytest>=6.0 ``. Finally, commands instruct tox to execute ``pytest `` in
91+ every environment.
9592
9693Executing tox
9794-------------
9895
99- Before you can run tox, you must ensure that you have installed it :
96+ Before you can run tox, you must ensure that you have installed tox-uv :
10097
10198.. tab :: Linux/macOS
10299
103100 .. code-block :: console
104101
105- $ python3 -m venv .venv
106- $ . .venv/bin/activate
107- $ python -m pip install tox
102+ $ uv sync --extra dev
108103
109104 .. tab :: Windows
110105
111106 .. code-block :: ps1con
112107
113- C:> python -m venv .venv
114- C:> .venv\Scripts\activate.bat
115- C:> python -m pip install tox
108+ C:> uv sync --extra dev
116109
117110 To run tox, simply start tox:
118111
119112.. code-block :: pytest
120113
121- $ python -m tox
122- 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
123- py313: commands[0]> coverage run -m pytest
114+ $ 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
116+ py313: commands[0]> python --version --version
124117 ============================= test session starts ==============================
125- platform darwin -- Python 3.13.0, pytest-8.3.3 , pluggy-1.5 .0
118+ platform darwin -- Python 3.13.0, pytest-8.4.1 , pluggy-1.6 .0
126119 cachedir: .tox/py313/.pytest_cache
127120 rootdir: /Users/veit/cusy/prj/items
128121 configfile: pyproject.toml
129122 testpaths: tests
130- plugins: cov-5.0.0, anyio-4.6.0, Faker-30.3.0
131- collected 49 items
132-
133- tests/api/test_add.py .... [ 8%]
134- tests/api/test_config.py . [ 10%]
135- tests/api/test_count.py ... [ 16%]
136- tests/api/test_delete.py ... [ 22%]
137- tests/api/test_finish.py .... [ 30%]
138- tests/api/test_list.py ......... [ 48%]
139- tests/api/test_start.py .... [ 57%]
140- tests/api/test_update.py .... [ 65%]
141- tests/api/test_version.py . [ 67%]
142- tests/cli/test_add.py .. [ 71%]
143- tests/cli/test_config.py .. [ 75%]
144- tests/cli/test_count.py . [ 77%]
145- tests/cli/test_delete.py . [ 79%]
146- tests/cli/test_errors.py .... [ 87%]
147- tests/cli/test_finish.py . [ 89%]
148- tests/cli/test_list.py .. [ 93%]
149- tests/cli/test_start.py . [ 95%]
150- tests/cli/test_update.py . [ 97%]
123+ plugins: anyio-4.9.0, Faker-37.4.0, cov-6.2.1
124+ collected 83 items
125+
126+ tests/api/test_add.py ...... [ 7%]
127+ tests/api/test_config.py . [ 8%]
128+ tests/api/test_count.py ... [ 12%]
129+ tests/api/test_delete.py ... [ 15%]
130+ tests/api/test_delete_all.py .. [ 18%]
131+ tests/api/test_exceptions.py .. [ 20%]
132+ tests/api/test_finish.py .... [ 25%]
133+ tests/api/test_item.py ... [ 28%]
134+ tests/api/test_item_id.py . [ 30%]
135+ tests/api/test_list.py ......... [ 40%]
136+ tests/api/test_list_edge_cases.py ........ [ 50%]
137+ tests/api/test_start.py .... [ 55%]
138+ tests/api/test_update.py ..... [ 61%]
139+ tests/api/test_version.py . [ 62%]
140+ tests/cli/test_add.py .. [ 65%]
141+ tests/cli/test_config.py .. [ 67%]
142+ tests/cli/test_count.py . [ 68%]
143+ tests/cli/test_delete.py . [ 69%]
144+ tests/cli/test_errors.py ....... [ 78%]
145+ tests/cli/test_finish.py . [ 79%]
146+ tests/cli/test_help.py ......... [ 90%]
147+ tests/cli/test_list.py ..... [ 96%]
148+ tests/cli/test_start.py . [ 97%]
149+ tests/cli/test_update.py . [ 98%]
151150 tests/cli/test_version.py . [100%]
152151
153- ============================== 49 passed in 0.16s ==============================
154- .pkg: _exit> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build
155- py313: OK ✔ in 1.48 seconds
156- congratulations :) (1.48 seconds)
152+ ============================== 83 passed in 0.27s ==============================
153+ py313: OK ✔ in 1.17 seconds
157154
158155 Testing multiple Python versions
159156--------------------------------
@@ -179,7 +176,7 @@ although I will only highlight the differences in the following illustration:
179176.. code-block :: pytest
180177 :emphasize-lines: 3-4, 8-12, 16-20, 24-28, 32-
181178
182- $ python -m tox
179+ $ uv run tox
183180 ...
184181 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
185182 py39: commands[0]> coverage run -m pytest
@@ -223,7 +220,7 @@ other. It is also possible to run them in parallel with the ``-p`` option:
223220
224221.. code-block :: pytest
225222
226- $ python -m tox -p
223+ $ uv run tox -p
227224 py310: SKIP ⚠ in 0.09 seconds
228225 py312: OK ✔ in 2.08 seconds
229226 py313: OK ✔ in 2.18 seconds
@@ -273,8 +270,8 @@ extend commands to ``pytest --cov=items``:
273270 coverage report
274271
275272 When using Coverage with ``tox ``, it can sometimes be useful to add a section in
276- the :file: `:file:` pyproject.toml`` file to tell Coverage which source code paths
277- should be considered identical:
273+ the :file: `pyproject.toml ` file to tell Coverage which source code paths should
274+ be considered identical:
278275
279276.. code-block :: ini
280277
@@ -289,7 +286,7 @@ example.
289286.. code-block :: console
290287 :emphasize-lines: 1
291288
292- $ python -m tox
289+ $ uv run tox
293290 ...
294291 coverage-report: commands[0]> coverage combine
295292 Combined data file .coverage.fay.local.19539.XpQXpsGx
@@ -366,7 +363,7 @@ keyword option. We also use ``--no-cov`` to disable coverage:
366363.. code-block :: pytest
367364 :emphasize-lines: 1, 3
368365
369- $ tox -e py313 -- -k test_version --no-cov
366+ $ uv run tox -e py313 -- -k test_version --no-cov
370367 ...
371368 py313: commands[0]> coverage run -m pytest -k test_version --no-cov
372369 ============================= test session starts ==============================
@@ -411,30 +408,38 @@ of environments are available for GitHub actions:
411408
412409 jobs :
413410 coverage :
414- name : Ensure 99 % test coverage
411+ name : Ensure 100 % test coverage
415412 runs-on : ubuntu-latest
416413 needs : tests
417414 if : always()
415+
418416 steps :
419417 - uses : actions/checkout@v4
418+ with :
419+ persist-credentials : false
420420 - uses : actions/setup-python@v5
421421 with :
422- cache : pip
423- python-version : 3.13
422+ python-version-file : .python-version
423+ - uses : hynek/setup-cached-uv@v2
424+
424425 - name : Download coverage data
425426 uses : actions/download-artifact@v4
426427 with :
427428 pattern : coverage-data-*
428429 merge-multiple : true
429- - name : Combine coverage and fail if it’s <99%.
430+
431+ - name : Combine coverage and fail if it’s <100%.
430432 run : |
431- python -m pip install --upgrade coverage[toml]
432- python -m coverage combine
433- python -m coverage html --skip-covered --skip-empty
433+ uv tool install coverage
434+
435+ coverage combine
436+ coverage html --skip-covered --skip-empty
437+
434438 # Report and write to summary.
435- python -m coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
436- # Report again and fail if under 99%.
437- python -m coverage report --fail-under=99
439+ coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
440+
441+ # Report again and fail if under 100%.
442+ coverage report --fail-under=100
438443
439444 ``name ``
440445 can be any name. It is displayed in the GitHub Actions user interface.
@@ -445,43 +450,16 @@ of environments are available for GitHub actions:
445450 is a GitHub actions tool that checks out our repository so that the rest
446451 of the workflow can access it.
447452 ``uses: actions/setup-python@v5 ``
448- is a GitHub actions tool that configures Python and installs it in a build
449- environment.
453+ is a GitHub actions tool that configures Python and installs it in a
454+ build environment.
450455 ``with: python-version: ${{ matrix.python }} ``
451- says that an environment should be created for each of the Python versions
452- listed in ``matrix.python ``.
453- ``run: python -m pip install tox tox-gh-actions ``
454- installs tox and simplifies the execution of tox in GitHub actions with
455- `tox-gh-actions <https://pypi.org/project/tox-gh-actions/ >`_ by providing
456- the environment that tox itself uses as the environment for the tests.
457- However, we still need to adjust our :file: `tox.ini ` file for this, for
458- example:
459-
460- .. code-block :: ini
461-
462- [gh-actions]
463- python =
464- 3.9: py39
465- 3.10: py310
466- 3.11: py311
467- 3.12: py312
468- 3.13: py313
469-
470- This assigns GitHub actions to tox environments.
471-
472- .. note ::
473- * You do not need to specify all variants of your environment. This
474- distinguishes ``tox-gh-actions `` from ``tox -e py ``.
475- * Make sure that the versions in the ``[gh-actions] `` section match the
476- available Python versions and, if applicable, those in the
477- :ref: `GitHub actions for Git pre-commit hooks
478- <gh-action-pre-commit-example>`.
479- * Since all tests for a specific Python version are executed one after
480- the other in a container, the advantages of parallel execution are
481- lost.
482-
483- ``run: python -m tox ``
484- executes tox.
456+ says that an environment should be created for each of the Python
457+ versions listed in ``matrix.python ``.
458+ ``uses: hynek/setup-cached-uv@v2 ``
459+ uses :term: `uv ` in GitHub Actions.
460+
461+ .. seealso ::
462+ * `setup-cached-uv <https://github.com/hynek/setup-cached-uv >`_
485463
486464#. You can then click on :guilabel: `Start commit `. As we want to make further
487465 changes before the tests are executed automatically, we select
@@ -494,18 +472,18 @@ of environments are available for GitHub actions:
494472The actions syntax is well documented. A good starting point in the GitHub
495473Actions documentation is the `Building and Testing Python
496474<https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-python> `__
497- page. The documentation also shows you how to run pytest directly without tox and
498- how to extend the matrix to multiple operating systems. As soon as you have set
499- up your :file: `*.yml ` file and uploaded it to your GitHub repository, it will be
500- executed automatically. You can then see the runs in the :menuselection: ` Actions `
501- tab:
475+ page. The documentation also shows you how to run pytest directly without tox
476+ and how to extend the matrix to multiple operating systems. As soon as you have
477+ set up your :file: `*.yml ` file and uploaded it to your GitHub repository, it
478+ will be executed automatically. You can then see the runs in the
479+ :menuselection: ` Actions ` tab:
502480
503481.. figure :: github-actions.png
504482 :alt: Screenshot of the GitHub actions overview
505483
506- The different Python environments are listed on the left-hand side. If you select
507- one, the results for this environment are displayed, as shown in the following
508- screenshot:
484+ The different Python environments are listed on the left-hand side. If you
485+ select one, the results for this environment are displayed, as shown in the
486+ following screenshot:
509487
510488.. figure :: github-actions-run.png
511489 :alt: Screenshot of a GitHub actions run for an environment
@@ -538,8 +516,8 @@ Extend tox
538516----------
539517
540518tox uses `pluggy <https://pluggy.readthedocs.io/en/stable/ >`_ to customise the
541- default behaviour. Pluggy finds a plugin by searching for an entry point with the
542- name ``tox ``, for example in a :file: `pyproject.toml ` file:
519+ default behaviour. Pluggy finds a plugin by searching for an entry point with
520+ the name ``tox ``, for example in a :file: `pyproject.toml ` file:
543521
544522.. code-block :: toml
545523
0 commit comments