Problem description
I'm writing a python library and I am planning to upload both sdist (.tar.gz) and wheel to PyPI. The build docs say that running
python -m build
I get sdist created from the source tree and wheel created from the sdist, which is nice since I get the sdist tested here "for free". Now I want to run tests (pytest) against the wheel with multiple python versions. What is the easiest way to do that?
I have been using tox and I see there's an option for setting package to "wheel":
[testenv]
description = run the tests with pytest
package = wheel
wheel_build_env = .pkg
But that does not say how the wheel is produced; I am unsure if it
a) creates wheel directly from source tree
b) creates wheel from sdist which is created from source tree in a way which is identical to python -m build
c) creates wheel from sdist which is created from source tree in a way which differs from python -m build
Even if the answer would be c), the wheel tested by tox would not be the same wheel that would be uploaded, so it is not testing the correct thing. Most likely I should somehow give the wheel as an argument to tox / test runner.
Question
I want to create a wheel from sdist which is created from the source tree, and I want to run unit tests against the wheel(s) with multiple python versions. This is a pure python project, so there will be only a single wheel per version of the package. What would be the idiomatic way to run the tests against the same wheel(s) which I would upload to PyPI? Can I use tox for that?
The Tox 4.12.2 documentation on External Package builder tells that it is possible to define an
externalpackage option (thanks for the comment @JürgenGmach). The external package option means that you setIn addition to this, one must create a section called
[.pkg_external](or<package_env>_externalif you have edited your package_env which has an aliasisolated_build_env). In this section, one should define at least thepackage_glob, which tells the tox where to install the wheel. If you also want to create the wheel, the you can do that in thecommandsoption of the[.pkg_external].Simple approach (multiple builds)
Example of a working configuration (tox 4.12.2):
python -m build) for each of your environments which do not haveskip_install=True. This has an open issue: tox #2729.Building wheel only once
It is also possible to make tox 4.14.2 build the wheel only once using the tox hooks. As can be seen from the Order of tox execution (in Appendix), one hook which can be used for this is the
tox_on_installfor the ".pkg_external" (either "requires" or "deps"). I use it to place a dummy file (/dist/.TOX-ASKS-REBUILD) which means that a build should be done. If that.TOX-ASKS-REBUILDexists, when the build script is ran, the/distfolder with all of its contents is removed, and new/distfolder with a .tar.gz and a .whl file is created.tox -e py311(if notskip_install=True)Hopefully this solution will become unnecessary at some point (when #2729 gets resolved)
The hook
toxfile.pyat project root.The tox_build_mypkg.py
/tests/tox_build_mypkg.pyThe tox.ini
Notes
pyproject.tomlas described in Extensions points. However, thetoxfile.pyis a bit handier as it does not have to be installed in the current environment.Appendix
Order of tox execution
The order of execution within tox can be reverse-engineered by using the dummy hook file defined in the Appendix (
tox_print_hooks.py) and the bullet point list about the order of execution in the System Overview. Note that I have set thepackage = externalalready which has some effect on the output. Here is what tox does:[1] N = number of environments in tox config file. The "2" comes from .pkg_external and .pkg_external_sdist_meta
[2] "First time" means: First time in this tox call. This is done only if there is at least one selected environment which does not have
skip_install=True.[3] This installs the package from wheel. If using the
package = externalin [testenv], it takes the wheel from the place defined by thepackage_globin the[testenv:.pkg_external]The dummy hook file
tox_print_hooks.py