Skip to content

Commit 1d44dad

Browse files
authored
Use Python "build" package as build frontend, don't directly run setup.py (#287)
* Use Python "build" package as build frontend, don't directly run setup.py Running setup.py directly is deprecated, from 2025-10-31 it won't be supported. The Python ecosystem is moving toward seperation of build frontend and backend. This commit switches to the "build" frontend, and keeps setuptools as the backend. Two `setup()` keywords have been altered `packages` was incomplete, but happened to work. Setuptools now warns about such a condition, which added noise in the build output. `test_suite` was unnnecessary because pytest is used for testing. It has been deprecated since by setuptools 41.5.0 (Oct 2019), and its presence emitted a warning that added noise to build output. This change should - allow wasmtime-py wheels to be built, published, and installed. It should not - alter versioning behaviour (specifically .devNNN tagging) - alter wheel contents It does not - allow building of an sdist (source tar.gz distribution) Risks - `-C=--build-option=--plat-name -C=--build-option=...` is ugly and possibly fragile. A future release of build or setuptools may break it. - No version constraints are placed on setuptools in pyproject.toml. I think this is the minimum viable change that fixes #286. Other improvements can be made seperately (e.g. migrating more metadata from setup.py to pyproject.toml). See - https://build.pypa.io/en/stable/ - https://packaging.python.org/en/latest/guides/writing-pyproject-toml/ - https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-test-suite - pypa/pip#11859 - pypa/setuptools#2491 - pypa/setuptools#3882 * Revert change to packages keyword in setup.py CI was failing because the "Setup wasmtime-py CI" action in .github/actions/ setup.yml installs `.[testing]` before ci/build-rust.py has been run. Thus wasmtime/bindgen/generated and wasmtime/bindgen/generated/imports didn't exist yet. See - https://github.com/bytecodealliance/wasmtime-py/actions/runs/15640817846/job/44067456534 * Build source distributions (sdist) alongside wheels (bdist_wheel) I have integrated running of the ci/download-wasmtime.py and ci/build-rsyt.py into the PEP-517 `build_wheel()` step. This allows a wheel to be built from just a source distribution (as opposed to a git checkout), so e.g. ``` pip install --no-binary=:any: wasmtime-py ``` has a chance of working. The integration mechanism is ci/_custom_build/backend.py, an in-tree PEP-517 build backend that extends the build backend provided by setuptools. This file is excluded from pytest discovery/scanning for expediency and to keep the scope of this change smaller. A future PR could remove this restriction to improve test and style guide coverage. I elected to keep most project metadata in setup.py for now, again to minimize the size of the PR. I recommend a future PR to move the metadata to pyproject.toml. This change incorporates the changes begun in PR #285, and supercedes it. As with that PR wheels builtin in wasmtime-pr should be unchanged - so in the common case end users do not require a rust compiler, or to build from source.
1 parent 31c3457 commit 1d44dad

File tree

6 files changed

+173
-39
lines changed

6 files changed

+173
-39
lines changed

.github/workflows/main.yml

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -48,55 +48,41 @@ jobs:
4848
# publishes to `test.pypi.org` works. (see `git` command in `setup.py`)
4949
fetch-depth: 0
5050
- uses: ./.github/actions/setup
51-
- run: pip install setuptools wheel
51+
- run: pip install build
5252
# If this is a tagged build use real version numbers
5353
- run: echo "PROD=true" >> $GITHUB_ENV
5454
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
55+
- name: Build sdist (source distribution)
56+
run: |
57+
git clean -fdx wasmtime build
58+
python -m build --sdist
5559
- run: |
5660
git clean -fdx wasmtime build
57-
python ci/download-wasmtime.py linux x86_64
58-
python ci/build-rust.py
59-
python setup.py bdist_wheel --plat-name manylinux1-x86_64
61+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=manylinux1-x86_64
6062
- run: |
6163
git clean -fdx wasmtime build
62-
python ci/download-wasmtime.py linux aarch64
63-
python ci/build-rust.py
64-
python setup.py bdist_wheel --plat-name manylinux2014_aarch64
64+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=manylinux2014_aarch64
6565
- run: |
6666
git clean -fdx wasmtime build
67-
python ci/download-wasmtime.py darwin x86_64
68-
python ci/build-rust.py
69-
python setup.py bdist_wheel --plat-name macosx-10-13-x86_64
67+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=macosx-10-13-x86_64
7068
- run: |
7169
git clean -fdx wasmtime build
72-
python ci/download-wasmtime.py darwin arm64
73-
python ci/build-rust.py
74-
python setup.py bdist_wheel --plat-name macosx-11-0-arm64
70+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=macosx-11-0-arm64
7571
- run: |
7672
git clean -fdx wasmtime build
77-
python ci/download-wasmtime.py win32 x86_64
78-
python ci/build-rust.py
79-
python setup.py bdist_wheel --plat-name win-amd64
73+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=win-amd64
8074
- run: |
8175
git clean -fdx wasmtime build
82-
python ci/download-wasmtime.py win32 arm64
83-
python ci/build-rust.py
84-
python setup.py bdist_wheel --plat-name win-arm64
76+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=win-arm64
8577
- run: |
8678
git clean -fdx wasmtime build
87-
python ci/download-wasmtime.py musl x86_64
88-
python ci/build-rust.py
89-
python setup.py bdist_wheel --plat-name musllinux_1_2_x86_64
79+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=musllinux_1_2_x86_64
9080
- run: |
9181
git clean -fdx wasmtime build
92-
python ci/download-wasmtime.py android x86_64
93-
python ci/build-rust.py
94-
python setup.py bdist_wheel --plat-name android_26_x86_64
82+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=android_26_x86_64
9583
- run: |
9684
git clean -fdx wasmtime build
97-
python ci/download-wasmtime.py android aarch64
98-
python ci/build-rust.py
99-
python setup.py bdist_wheel --plat-name android_26_arm64_v8a
85+
python -m build --wheel -C=--build-option=--plat-name -C=--build-option=android_26_arm64_v8a
10086
10187
# Build an "any" wheel with:
10288
#
@@ -109,13 +95,11 @@ jobs:
10995
# shared libraries.
11096
- run: |
11197
git clean -fdx wasmtime build
112-
python ci/download-wasmtime.py win32 x86_64
113-
python ci/build-rust.py
114-
python setup.py bdist_wheel
98+
python -m build --wheel -C=--wasmtime-py-mingw-any
11599
116100
- uses: actions/upload-artifact@v4
117101
with:
118-
name: wheels
102+
name: dists
119103
path: dist
120104

121105
docs:
@@ -164,7 +148,7 @@ jobs:
164148
with:
165149
path: generated-docs
166150

167-
upload_wheels:
151+
upload_dists:
168152
needs: build
169153
runs-on: ubuntu-latest
170154
steps:
@@ -176,7 +160,7 @@ jobs:
176160
- uses: ./.github/actions/setup
177161
- uses: actions/download-artifact@v4
178162
with:
179-
name: wheels
163+
name: dists
180164
path: dist
181165
- run: find .
182166

MANIFEST.in

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,31 @@
1-
recursive-include wasmtime *.py *.wasm *.so *.dll *.dylib
1+
# Specify files to be included in source distributions (sdist).
2+
# Some files are included by default already (e.g. LICENSE, README*).
3+
# https://setuptools.pypa.io/en/stable/userguide/miscellaneous.html#controlling-files-in-the-distribution
4+
5+
graft ci
6+
graft examples
7+
graft rust
8+
graft tests
9+
10+
prune rust/target
11+
prune tests/bindgen/generated
12+
prune tests/codegen/generated
13+
14+
# Wasmtime C headers. Downloaded by ci/download-wasmtime.py
15+
prune wasmtime/include
16+
17+
# WebAssmbly modules, python bindings. Generated by ci/build-rust.py
18+
recursive-exclude wasmtime/bindgen/generated *.py *.wasm
19+
20+
include .flake8
21+
include CONTRIBUTING.md
22+
include mypy.ini
23+
include pytest.ini
24+
25+
# Wasmtime shared library. Downloaded by ci/download-wasmtime.py
26+
# Included in binary distributions
27+
global-exclude *.dll
28+
global-exclude *.dylib
29+
global-exclude *.py[cdo]
30+
global-exclude *.so
31+
global-exclude *~

ci/_custom_build/backend.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
An in-tree PEP-517 build backend that downloads wasmtime and generates Python
3+
bindings. It extends the PEP-517 backend provided by setuptools.
4+
5+
See
6+
- https://peps.python.org/pep-0517/#build-backend-interface
7+
- https://setuptools.pypa.io/en/latest/build_meta.html#dynamic-build-dependencies-and-other-build-meta-tweaks
8+
"""
9+
10+
import subprocess
11+
import sys
12+
13+
from typing import Union
14+
15+
# `from ... import *` is intentional and necessary, so that any PEP-517 hooks
16+
# not overridden here are still available to build frontends.
17+
from setuptools import build_meta as build_meta_orig
18+
from setuptools.build_meta import *
19+
20+
21+
def get_plat_name(config_settings:Union[dict, None]) -> Union[str, None]:
22+
"""
23+
Return the value of any --plat-name passed to the build frontend,
24+
otherwise None.
25+
26+
>>> get_plat_name({'--build-option': ['--plat-name', 'manylinux1-x86_64']})
27+
'manylinux1-x86_64'
28+
>>> print(get_plat_name(None))
29+
None
30+
>>> print(get_plat_name({}))
31+
None
32+
>>> print(get_plat_name({'--build-option': []}))
33+
None
34+
"""
35+
if config_settings is None:
36+
return None
37+
38+
try:
39+
build_options = config_settings['--build-option']
40+
except KeyError:
41+
return None
42+
43+
try:
44+
plat_name_option_idx = build_options.index('--plat-name')
45+
except ValueError:
46+
return None
47+
48+
plat_name = build_options[plat_name_option_idx+1]
49+
return plat_name
50+
51+
52+
def plat_name_to_download_args(plat_name:str) -> list[str]:
53+
"Return a list of download-wasmtime.py arguments for a given platform name."
54+
mapping = {
55+
'android_26_arm64_v8a': ['android', 'aarch64'],
56+
'android_26_x86_64': ['android', 'x86_64'],
57+
'macosx-10-13-x86_64': ['darwin', 'x86_64'],
58+
'macosx-11-0-arm64': ['darwin', 'arm64'],
59+
'manylinux1-x86_64': ['linux', 'x86_64'],
60+
'manylinux2014_aarch64': ['linux', 'aarch64'],
61+
'musllinux_1_2_x86_64': ['musl', 'x86_64'],
62+
'win-amd64': ['win32', 'x86_64'],
63+
'win-arm64': ['win32', 'arm64'],
64+
}
65+
return mapping[plat_name]
66+
67+
68+
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
69+
"""
70+
Download a pre-compiled build of wasmtime and generate bindings, both to be
71+
included in the output Python wheel.
72+
73+
This is a PEP-517 build backend function.
74+
"""
75+
plat_name = get_plat_name(config_settings)
76+
if plat_name is not None:
77+
download_args = plat_name_to_download_args(plat_name)
78+
elif config_settings is not None and '--wasmtime-py-mingw-any' in config_settings:
79+
download_args = ['win32', 'x86_64']
80+
else:
81+
download_args = []
82+
83+
subprocess.run(
84+
[sys.executable, 'ci/download-wasmtime.py', *download_args],
85+
check=True,
86+
)
87+
subprocess.run(
88+
[sys.executable, 'ci/build-rust.py'],
89+
check=True,
90+
)
91+
92+
return build_meta_orig.build_wheel(wheel_directory, config_settings, metadata_directory)

pyproject.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[build-system]
2+
requires = [
3+
"setuptools",
4+
]
5+
# Use an in-tree build backend, to customise the wheels we generate
6+
build-backend = "backend"
7+
backend-path = [
8+
"ci/_custom_build",
9+
]

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[pytest]
22
addopts = --doctest-modules --mypy --ignore-glob=tests/bindgen/*/app.py
33
norecursedirs =
4+
ci/_custom_build
45
tests/bindgen/generated/*

setup.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
# Give unique version numbers to all commits so our publication-on-each commit
1313
# works on main
14-
if 'PROD' not in os.environ:
14+
# TODO Temporarily disable '.devNNN' version suffix while porting to PEP-517
15+
# https://github.com/bytecodealliance/wasmtime-py/pull/287#issuecomment-2979288710
16+
if False and 'PROD' not in os.environ:
1517
res = subprocess.run(['git', 'rev-list', 'HEAD', '--count'], capture_output=True, encoding="utf8")
1618
version += '.dev' + res.stdout.strip()
1719

@@ -30,12 +32,28 @@
3032
"Documentation": "https://bytecodealliance.github.io/wasmtime-py/",
3133
"Source Code": "https://github.com/bytecodealliance/wasmtime-py",
3234
},
33-
packages=['wasmtime'],
35+
packages=[
36+
'wasmtime',
37+
'wasmtime.bindgen',
38+
],
3439
install_requires=['importlib_resources>=5.10'],
3540
include_package_data=True,
36-
package_data={"wasmtime": ["py.typed"]},
41+
package_data={
42+
"wasmtime": [
43+
# Wasmtime shared library. Downloaded by ci/download-wasmtime.py
44+
"*-*/*.dll",
45+
"*-*/*.dylib",
46+
"*-*/*.so",
47+
"py.typed",
48+
],
49+
"wasmtime.bindgen": [
50+
# WebAssmbly modules, python bindings. Generated by ci/build-rust.py
51+
"generated/*.py",
52+
"generated/*.wasm",
53+
"generated/imports/*.py",
54+
],
55+
},
3756
python_requires='>=3.9',
38-
test_suite="tests",
3957
extras_require={
4058
'testing': [
4159
'coverage',

0 commit comments

Comments
 (0)