Skip to content

Commit 5e2d02d

Browse files
authored
Merge branch 'main' into new_podman_support
2 parents a361670 + d02366f commit 5e2d02d

File tree

12 files changed

+285
-108
lines changed

12 files changed

+285
-108
lines changed

Diff for: .github/dependabot.yml

-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,3 @@ updates:
55
directory: "/"
66
schedule:
77
interval: "weekly"
8-
ignore:
9-
- dependency-name: "actions/*"
10-
update-types: ["version-update:semver-minor", "version-update:semver-patch"]

Diff for: bin/bump_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def bump_version() -> None:
184184
print()
185185

186186
release_url = "https://github.com/pypa/cibuildwheel/releases/new?" + urllib.parse.urlencode(
187-
{"tag": new_version}
187+
{"tag": f"v{new_version}"}
188188
)
189189
print("Then create a release at the URL:")
190190
print(f" {release_url}")

Diff for: cibuildwheel/linux.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .util import (
1414
BuildSelector,
1515
NonPlatformWheelError,
16-
find_compatible_abi3_wheel,
16+
find_compatible_wheel,
1717
get_build_verbosity_extra_flags,
1818
prepare_command,
1919
read_python_configs,
@@ -183,13 +183,13 @@ def build_in_container(
183183
)
184184
sys.exit(1)
185185

186-
abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
187-
if abi3_wheel:
186+
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
187+
if compatible_wheel:
188188
log.step_end()
189189
print(
190-
f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
190+
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
191191
)
192-
repaired_wheels = [abi3_wheel]
192+
repaired_wheels = [compatible_wheel]
193193
else:
194194

195195
if build_options.before_build:
@@ -312,7 +312,7 @@ def build_in_container(
312312
container.call(["rm", "-rf", venv_dir])
313313

314314
# move repaired wheels to output
315-
if abi3_wheel is None:
315+
if compatible_wheel is None:
316316
container.call(["mkdir", "-p", container_output_dir])
317317
container.call(["mv", *repaired_wheels, container_output_dir])
318318
built_wheels.extend(

Diff for: cibuildwheel/macos.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
call,
2525
detect_ci_provider,
2626
download,
27-
find_compatible_abi3_wheel,
27+
find_compatible_wheel,
2828
get_build_verbosity_extra_flags,
2929
get_pip_version,
3030
install_certifi_script,
@@ -323,13 +323,13 @@ def build(options: Options, tmp_path: Path) -> None:
323323
build_options.build_frontend,
324324
)
325325

326-
abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
327-
if abi3_wheel:
326+
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
327+
if compatible_wheel:
328328
log.step_end()
329329
print(
330-
f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
330+
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
331331
)
332-
repaired_wheel = abi3_wheel
332+
repaired_wheel = compatible_wheel
333333
else:
334334
if build_options.before_build:
335335
log.step("Running before_build...")
@@ -536,7 +536,7 @@ def build(options: Options, tmp_path: Path) -> None:
536536
)
537537

538538
# we're all done here; move it to output (overwrite existing)
539-
if abi3_wheel is None:
539+
if compatible_wheel is None:
540540
try:
541541
(build_options.output_dir / repaired_wheel.name).unlink()
542542
except FileNotFoundError:

Diff for: cibuildwheel/util.py

+24-13
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"MANYLINUX_ARCHS",
5454
"call",
5555
"shell",
56-
"find_compatible_abi3_wheel",
56+
"find_compatible_wheel",
5757
"format_safe",
5858
"prepare_command",
5959
"get_build_verbosity_extra_flags",
@@ -575,36 +575,47 @@ def virtualenv(
575575
T = TypeVar("T", bound=PurePath)
576576

577577

578-
def find_compatible_abi3_wheel(wheels: Sequence[T], identifier: str) -> Optional[T]:
578+
def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> Optional[T]:
579579
"""
580-
Finds an ABI3 wheel in `wheels` compatible with the Python interpreter
581-
specified by `identifier`.
580+
Finds a wheel with an abi3 or a none ABI tag in `wheels` compatible with the Python interpreter
581+
specified by `identifier` that is previously built.
582582
"""
583583

584584
interpreter, platform = identifier.split("-")
585-
if not interpreter.startswith("cp3"):
586-
return None
587585
for wheel in wheels:
588586
_, _, _, tags = parse_wheel_filename(wheel.name)
589587
for tag in tags:
590-
if tag.abi != "abi3":
591-
continue
592-
if not tag.interpreter.startswith("cp3"):
588+
if tag.abi == "abi3":
589+
# ABI3 wheels must start with cp3 for impl and tag
590+
if not (interpreter.startswith("cp3") and tag.interpreter.startswith("cp3")):
591+
continue
592+
elif tag.abi == "none":
593+
# CPythonless wheels must include py3 tag
594+
if tag.interpreter[:3] != "py3":
595+
continue
596+
else:
597+
# Other types of wheels are not detected, this is looking for previously built wheels.
593598
continue
594-
if int(tag.interpreter[3:]) > int(interpreter[3:]):
599+
600+
if tag.interpreter != "py3" and int(tag.interpreter[3:]) > int(interpreter[3:]):
601+
# If a minor version number is given, it has to be lower than the current one.
595602
continue
603+
596604
if platform.startswith(("manylinux", "musllinux", "macosx")):
597-
# Linux, macOS
605+
# Linux, macOS require the beginning and ending match (macos/manylinux version doesn't need to)
598606
os_, arch = platform.split("_", 1)
599607
if not tag.platform.startswith(os_):
600608
continue
601-
if not tag.platform.endswith("_" + arch):
609+
if not tag.platform.endswith(f"_{arch}"):
602610
continue
603611
else:
604-
# Windows
612+
# Windows should exactly match
605613
if not tag.platform == platform:
606614
continue
615+
616+
# If all the filters above pass, then the wheel is a previously built compatible wheel.
607617
return wheel
618+
608619
return None
609620

610621

Diff for: cibuildwheel/windows.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
NonPlatformWheelError,
2424
call,
2525
download,
26-
find_compatible_abi3_wheel,
26+
find_compatible_wheel,
2727
get_build_verbosity_extra_flags,
2828
get_pip_version,
2929
prepare_command,
@@ -279,13 +279,13 @@ def build(options: Options, tmp_path: Path) -> None:
279279
build_options.build_frontend,
280280
)
281281

282-
abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
283-
if abi3_wheel:
282+
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
283+
if compatible_wheel:
284284
log.step_end()
285285
print(
286-
f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
286+
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
287287
)
288-
repaired_wheel = abi3_wheel
288+
repaired_wheel = compatible_wheel
289289
else:
290290
# run the before_build command
291291
if build_options.before_build:
@@ -420,7 +420,7 @@ def build(options: Options, tmp_path: Path) -> None:
420420
shell(test_command_prepared, cwd="c:\\", env=virtualenv_env)
421421

422422
# we're all done here; move it to output (remove if already exists)
423-
if abi3_wheel is None:
423+
if compatible_wheel is None:
424424
shutil.move(str(repaired_wheel), build_options.output_dir)
425425
built_wheels.append(build_options.output_dir / repaired_wheel.name)
426426

Diff for: docs/faq.md

-4
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,6 @@ updates:
147147
directory: "/"
148148
schedule:
149149
interval: "weekly"
150-
ignore:
151-
# Optional: Official actions have moving tags like v1;
152-
# if you use those, you don't need updates.
153-
- dependency-name: "actions/*"
154150
```
155151

156152
#### Option 2: Requirement files

Diff for: setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ console_scripts =
5656
cibuildwheel = resources/*
5757

5858
[flake8]
59-
extend-ignore = E203,E501,B950
60-
extend-select = B,B9
59+
extend-ignore = E203,E501,B950,B023
60+
extend-select = B9
6161
application-import-names = cibuildwheel
6262
exclude =
6363
cibuildwheel/resources/,

Diff for: test/test_abi_variants.py

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import textwrap
2+
3+
from . import test_projects, utils
4+
5+
limited_api_project = test_projects.new_c_project(
6+
setup_py_add=textwrap.dedent(
7+
r"""
8+
cmdclass = {}
9+
extension_kwargs = {}
10+
if sys.version_info[:2] >= (3, 8):
11+
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
12+
13+
class bdist_wheel_abi3(_bdist_wheel):
14+
def finalize_options(self):
15+
_bdist_wheel.finalize_options(self)
16+
self.root_is_pure = False
17+
18+
def get_tag(self):
19+
python, abi, plat = _bdist_wheel.get_tag(self)
20+
return python, "abi3", plat
21+
22+
cmdclass["bdist_wheel"] = bdist_wheel_abi3
23+
extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x03080000")]
24+
extension_kwargs["py_limited_api"] = True
25+
"""
26+
),
27+
setup_py_extension_args_add="**extension_kwargs",
28+
setup_py_setup_args_add="cmdclass=cmdclass",
29+
)
30+
31+
32+
def test_abi3(tmp_path):
33+
project_dir = tmp_path / "project"
34+
limited_api_project.generate(project_dir)
35+
36+
# build the wheels
37+
actual_wheels = utils.cibuildwheel_run(
38+
project_dir,
39+
add_env={
40+
"CIBW_SKIP": "pp* ", # PyPy does not have a Py_LIMITED_API equivalent
41+
},
42+
)
43+
44+
# check that the expected wheels are produced
45+
expected_wheels = [
46+
w.replace("cp38-cp38", "cp38-abi3")
47+
for w in utils.expected_wheels("spam", "0.1.0")
48+
if "-pp" not in w and "-cp39" not in w and "-cp310" not in w and "-cp311" not in w
49+
]
50+
assert set(actual_wheels) == set(expected_wheels)
51+
52+
53+
ctypes_project = test_projects.TestProject()
54+
ctypes_project.files["setup.py"] = textwrap.dedent(
55+
"""
56+
from setuptools import setup, Extension
57+
58+
from distutils.command.build_ext import build_ext as _build_ext
59+
class CTypesExtension(Extension): pass
60+
class build_ext(_build_ext):
61+
def build_extension(self, ext):
62+
self._ctypes = isinstance(ext, CTypesExtension)
63+
return super().build_extension(ext)
64+
65+
def get_export_symbols(self, ext):
66+
if self._ctypes:
67+
return ext.export_symbols
68+
return super().get_export_symbols(ext)
69+
70+
def get_ext_filename(self, ext_name):
71+
if self._ctypes:
72+
return ext_name + '.so'
73+
return super().get_ext_filename(ext_name)
74+
75+
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
76+
class bdist_wheel_abi_none(_bdist_wheel):
77+
def finalize_options(self):
78+
_bdist_wheel.finalize_options(self)
79+
self.root_is_pure = False
80+
81+
def get_tag(self):
82+
python, abi, plat = _bdist_wheel.get_tag(self)
83+
return "py3", "none", plat
84+
85+
setup(
86+
name="ctypesexample",
87+
version="1.0.0",
88+
py_modules = ["ctypesexample.summing"],
89+
ext_modules=[
90+
CTypesExtension(
91+
"ctypesexample.csumlib",
92+
["ctypesexample/csumlib.c"],
93+
),
94+
],
95+
cmdclass={'build_ext': build_ext, 'bdist_wheel': bdist_wheel_abi_none},
96+
)
97+
"""
98+
)
99+
ctypes_project.files["ctypesexample/csumlib.c"] = textwrap.dedent(
100+
"""
101+
#ifdef _WIN32
102+
#define LIBRARY_API __declspec(dllexport)
103+
#else
104+
#define LIBRARY_API
105+
#endif
106+
107+
#include <stdlib.h>
108+
109+
110+
LIBRARY_API double *add_vec3(double *a, double *b)
111+
{
112+
double *res = malloc(sizeof(double) * 3);
113+
114+
for (int i = 0; i < 3; ++i)
115+
{
116+
res[i] = a[i] + b[i];
117+
}
118+
119+
return res;
120+
}
121+
"""
122+
)
123+
ctypes_project.files["ctypesexample/summing.py"] = textwrap.dedent(
124+
"""
125+
import ctypes
126+
import pathlib
127+
128+
# path of the shared library
129+
libfile = pathlib.Path(__file__).parent / "csumlib.so"
130+
csumlib = ctypes.CDLL(str(libfile))
131+
132+
type_vec3 = ctypes.POINTER(ctypes.c_double * 3)
133+
134+
csumlib.add_vec3.restype = type_vec3
135+
csumlib.add_vec3.argtypes = [type_vec3, type_vec3]
136+
def add(a: list, b: list) -> list:
137+
a_p = (ctypes.c_double * 3)(*a)
138+
b_p = (ctypes.c_double * 3)(*b)
139+
r_p = csumlib.add_vec3(a_p,b_p)
140+
141+
return [l for l in r_p.contents]
142+
"""
143+
)
144+
145+
ctypes_project.files["test/add_test.py"] = textwrap.dedent(
146+
"""
147+
import ctypesexample.summing
148+
149+
def test():
150+
a = [1, 2, 3]
151+
b = [4, 5, 6]
152+
assert ctypesexample.summing.add(a, b) == [5, 7, 9]
153+
"""
154+
)
155+
156+
157+
def test_abi_none(tmp_path, capfd):
158+
project_dir = tmp_path / "project"
159+
ctypes_project.generate(project_dir)
160+
161+
# build the wheels
162+
actual_wheels = utils.cibuildwheel_run(
163+
project_dir,
164+
add_env={
165+
"CIBW_TEST_REQUIRES": "pytest",
166+
"CIBW_TEST_COMMAND": "pytest {project}/test",
167+
# limit the number of builds for test performance reasons
168+
"CIBW_BUILD": "cp38-* cp310-* pp39-*",
169+
},
170+
)
171+
172+
# check that the expected wheels are produced
173+
expected_wheels = utils.expected_wheels("ctypesexample", "1.0.0", python_abi_tags=["py3-none"])
174+
assert set(actual_wheels) == set(expected_wheels)
175+
176+
# check that each wheel was built once, and reused
177+
captured = capfd.readouterr()
178+
assert "Building wheel..." in captured.out
179+
assert "Found previously built wheel" in captured.out

0 commit comments

Comments
 (0)