Skip to content

Commit dd20934

Browse files
authored
Allow initializing WheelPolicies with a specific libc & architecture (#470)
1 parent 3c0a415 commit dd20934

File tree

3 files changed

+91
-26
lines changed

3 files changed

+91
-26
lines changed

src/auditwheel/policy/__init__.py

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
_HERE = Path(__file__).parent
1919
LIBPYTHON_RE = re.compile(r"^libpython\d+\.\d+m?.so(.\d)*$")
20+
_MUSL_POLICY_RE = re.compile(r"^musllinux_\d+_\d+$")
2021

2122
logger = logging.getLogger(__name__)
2223

@@ -30,14 +31,30 @@
3031

3132

3233
class WheelPolicies:
33-
def __init__(self) -> None:
34-
libc_variant = get_libc()
35-
policies_path = _POLICY_JSON_MAP[libc_variant]
36-
policies = json.loads(policies_path.read_text())
34+
def __init__(
35+
self,
36+
*,
37+
libc: Libc | None = None,
38+
musl_policy: str | None = None,
39+
arch: str | None = None,
40+
) -> None:
41+
if libc is None:
42+
libc = get_libc() if musl_policy is None else Libc.MUSL
43+
if libc != Libc.MUSL and musl_policy is not None:
44+
raise ValueError(f"'musl_policy' shall be None for libc {libc.name}")
45+
if libc == Libc.MUSL:
46+
if musl_policy is None:
47+
musl_version = get_musl_version(find_musl_libc())
48+
musl_policy = f"musllinux_{musl_version.major}_{musl_version.minor}"
49+
elif _MUSL_POLICY_RE.match(musl_policy) is None:
50+
raise ValueError(f"Invalid 'musl_policy': '{musl_policy}'")
51+
if arch is None:
52+
arch = get_arch_name()
53+
policies = json.loads(_POLICY_JSON_MAP[libc].read_text())
3754
self._policies = []
38-
self._musl_policy = _get_musl_policy()
39-
self._arch_name = get_arch_name()
40-
self._libc_variant = get_libc()
55+
self._arch_name = arch
56+
self._libc_variant = libc
57+
self._musl_policy = musl_policy
4158

4259
_validate_pep600_compliance(policies)
4360
for policy in policies:
@@ -59,7 +76,7 @@ def __init__(self) -> None:
5976
alias + "_" + self._arch_name for alias in policy["aliases"]
6077
]
6178
policy["lib_whitelist"] = _fixup_musl_libc_soname(
62-
policy["lib_whitelist"]
79+
libc, arch, policy["lib_whitelist"]
6380
)
6481
self._policies.append(policy)
6582

@@ -219,10 +236,6 @@ def get_arch_name() -> str:
219236
return machine
220237

221238

222-
_ARCH_NAME = get_arch_name()
223-
_LIBC = get_libc()
224-
225-
226239
def _validate_pep600_compliance(policies) -> None:
227240
symbol_versions: dict[str, dict[str, set[str]]] = {}
228241
lib_whitelist: set[str] = set()
@@ -253,15 +266,8 @@ def _validate_pep600_compliance(policies) -> None:
253266
symbol_versions[arch] = symbol_versions_arch
254267

255268

256-
def _get_musl_policy():
257-
if _LIBC != Libc.MUSL:
258-
return None
259-
musl_version = get_musl_version(find_musl_libc())
260-
return f"musllinux_{musl_version.major}_{musl_version.minor}"
261-
262-
263-
def _fixup_musl_libc_soname(whitelist):
264-
if _LIBC != Libc.MUSL:
269+
def _fixup_musl_libc_soname(libc: Libc, arch: str, whitelist):
270+
if libc != Libc.MUSL:
265271
return whitelist
266272
soname_map = {
267273
"libc.so": {
@@ -276,7 +282,7 @@ def _fixup_musl_libc_soname(whitelist):
276282
new_whitelist = []
277283
for soname in whitelist:
278284
if soname in soname_map:
279-
new_soname = soname_map[soname][_ARCH_NAME]
285+
new_soname = soname_map[soname][arch]
280286
logger.debug(f"Replacing whitelisted '{soname}' by '{new_soname}'")
281287
new_whitelist.append(new_soname)
282288
else:

tests/integration/test_bundled_wheels.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
import pytest
1414

1515
from auditwheel import main_repair
16+
from auditwheel.libc import Libc
1617
from auditwheel.policy import WheelPolicies
1718
from auditwheel.wheel_abi import analyze_wheel_abi
1819

1920
HERE = Path(__file__).parent.resolve()
2021

2122

22-
@pytest.mark.skipif(platform.machine() != "x86_64", reason="only supported on x86_64")
2323
@pytest.mark.parametrize(
2424
"file, external_libs",
2525
[
@@ -28,14 +28,13 @@
2828
],
2929
)
3030
def test_analyze_wheel_abi(file, external_libs):
31-
wheel_policies = WheelPolicies()
31+
wheel_policies = WheelPolicies(libc=Libc.GLIBC, arch="x86_64")
3232
winfo = analyze_wheel_abi(wheel_policies, str(HERE / file))
3333
assert set(winfo.external_refs["manylinux_2_5_x86_64"]["libs"]) == external_libs
3434

3535

36-
@pytest.mark.skipif(platform.machine() != "x86_64", reason="only supported on x86_64")
3736
def test_analyze_wheel_abi_pyfpe():
38-
wheel_policies = WheelPolicies()
37+
wheel_policies = WheelPolicies(libc=Libc.GLIBC, arch="x86_64")
3938
winfo = analyze_wheel_abi(
4039
wheel_policies, str(HERE / "fpewheel-0.0.0-cp35-cp35m-linux_x86_64.whl")
4140
)

tests/unit/test_policy.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,37 @@
11
from __future__ import annotations
22

3+
import re
4+
from contextlib import nullcontext as does_not_raise
35
from unittest.mock import patch
46

57
import pytest
68

9+
from auditwheel.error import InvalidLibc
10+
from auditwheel.libc import Libc
711
from auditwheel.policy import (
812
WheelPolicies,
913
_validate_pep600_compliance,
1014
get_arch_name,
15+
get_libc,
1116
get_replace_platforms,
1217
)
1318

1419

20+
def ids(x):
21+
if isinstance(x, Libc):
22+
return x.name
23+
if isinstance(x, does_not_raise):
24+
return "NoError"
25+
if hasattr(x, "expected_exception"):
26+
return x.expected_exception
27+
28+
29+
def raises(exception, match=None, escape=True):
30+
if escape and match is not None:
31+
match = re.escape(match)
32+
return pytest.raises(exception, match=match)
33+
34+
1535
@patch("auditwheel.policy._platform_module.machine")
1636
@patch("auditwheel.policy.bits", 32)
1737
@pytest.mark.parametrize(
@@ -233,3 +253,43 @@ def test_filter_libs(self):
233253
# Assert that each policy only has the unfiltered libs.
234254
for policy in full_external_refs:
235255
assert set(full_external_refs[policy]["libs"]) == set(unfiltered_libs)
256+
257+
258+
@pytest.mark.parametrize(
259+
"libc,musl_policy,arch,exception",
260+
[
261+
# valid
262+
(None, None, None, does_not_raise()),
263+
(Libc.GLIBC, None, None, does_not_raise()),
264+
(Libc.MUSL, "musllinux_1_1", None, does_not_raise()),
265+
(None, "musllinux_1_1", None, does_not_raise()),
266+
(None, None, "aarch64", does_not_raise()),
267+
# invalid
268+
(
269+
Libc.GLIBC,
270+
"musllinux_1_1",
271+
None,
272+
raises(ValueError, "'musl_policy' shall be None"),
273+
),
274+
(Libc.MUSL, "manylinux_1_1", None, raises(ValueError, "Invalid 'musl_policy'")),
275+
(Libc.MUSL, "musllinux_5_1", None, raises(AssertionError)),
276+
(Libc.MUSL, "musllinux_1_1", "foo", raises(AssertionError)),
277+
# platform dependant
278+
(
279+
Libc.MUSL,
280+
None,
281+
None,
282+
does_not_raise() if get_libc() == Libc.MUSL else raises(InvalidLibc),
283+
),
284+
],
285+
ids=ids,
286+
)
287+
def test_wheel_policies_args(libc, musl_policy, arch, exception):
288+
with exception:
289+
wheel_policies = WheelPolicies(libc=libc, musl_policy=musl_policy, arch=arch)
290+
if libc is not None:
291+
assert wheel_policies._libc_variant == libc
292+
if musl_policy is not None:
293+
assert wheel_policies._musl_policy == musl_policy
294+
if arch is not None:
295+
assert wheel_policies._arch_name == arch

0 commit comments

Comments
 (0)