From dc74991f4773c1538d5d477b6f4996005543cb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20Kiss=20Koll=C3=A1r?= Date: Wed, 1 Nov 2023 20:30:58 +0000 Subject: [PATCH] Remove globals in policy module The policy module creates several globals on the module level, which makes it very difficult to test code which interacts with the policies. This patch creates a `WheelPolicies` class which encapsulates the policy processing code and exposes functions to query the parsed policies. Unfortunately, a lot of functions directly operate on policies and these all need a new argument to take the new policy object. This change should make testing substantially easier as policies can be easily created on the fly and passed down to functions which require these. --- src/auditwheel/main_repair.py | 30 ++-- src/auditwheel/main_show.py | 30 ++-- src/auditwheel/policy/__init__.py | 154 +++++++++++-------- src/auditwheel/policy/external_references.py | 8 +- src/auditwheel/policy/versioned_symbols.py | 6 +- src/auditwheel/repair.py | 5 +- src/auditwheel/wheel_abi.py | 51 +++--- tests/integration/test_bundled_wheels.py | 9 +- tests/integration/test_manylinux.py | 7 +- tests/integration/test_policy_files.py | 30 ++-- tests/unit/test_policy.py | 68 ++++---- tests/unit/test_wheel_abi.py | 4 +- 12 files changed, 217 insertions(+), 185 deletions(-) diff --git a/src/auditwheel/main_repair.py b/src/auditwheel/main_repair.py index b1d9f878..0168d825 100644 --- a/src/auditwheel/main_repair.py +++ b/src/auditwheel/main_repair.py @@ -6,20 +6,15 @@ from auditwheel.patcher import Patchelf -from .policy import ( - POLICY_PRIORITY_HIGHEST, - get_policy_by_name, - get_policy_name, - get_priority_by_name, - load_policies, -) +from .policy import WheelPolicies from .tools import EnvironmentDefault logger = logging.getLogger(__name__) def configure_parser(sub_parsers): - policies = load_policies() + wheel_policy = WheelPolicies.load() + policies = wheel_policy.policies policy_names = [p["name"] for p in policies] policy_names += [alias for p in policies for alias in p["aliases"]] epilog = """PLATFORMS: @@ -32,7 +27,7 @@ def configure_parser(sub_parsers): if len(p["aliases"]) > 0: epilog += f" (aliased by {', '.join(p['aliases'])})" epilog += "\n" - highest_policy = get_policy_name(POLICY_PRIORITY_HIGHEST) + highest_policy = wheel_policy.get_policy_name(wheel_policy.priority_highest) help = """Vendor in external shared library dependencies of a wheel. If multiple wheels are specified, an error processing one wheel will abort processing of subsequent wheels. @@ -114,6 +109,8 @@ def execute(args, p): from .repair import repair_wheel from .wheel_abi import NonPlatformWheel, analyze_wheel_abi + wheel_policy = WheelPolicies.load() + for wheel_file in args.WHEEL_FILE: if not isfile(wheel_file): p.error("cannot access %s. No such file" % wheel_file) @@ -124,15 +121,15 @@ def execute(args, p): os.makedirs(args.WHEEL_DIR) try: - wheel_abi = analyze_wheel_abi(wheel_file) + wheel_abi = analyze_wheel_abi(wheel_policy, wheel_file) except NonPlatformWheel: logger.info(NonPlatformWheel.LOG_MESSAGE) return 1 - policy = get_policy_by_name(args.PLAT) + policy = wheel_policy.get_policy_by_name(args.PLAT) reqd_tag = policy["priority"] - if reqd_tag > get_priority_by_name(wheel_abi.sym_tag): + if reqd_tag > wheel_policy.get_priority_by_name(wheel_abi.sym_tag): msg = ( 'cannot repair "%s" to "%s" ABI because of the presence ' "of too-recent versioned symbols. You'll need to compile " @@ -140,7 +137,7 @@ def execute(args, p): ) p.error(msg) - if reqd_tag > get_priority_by_name(wheel_abi.ucs_tag): + if reqd_tag > wheel_policy.get_priority_by_name(wheel_abi.ucs_tag): msg = ( 'cannot repair "%s" to "%s" ABI because it was compiled ' "against a UCS2 build of Python. You'll need to compile " @@ -149,7 +146,7 @@ def execute(args, p): ) p.error(msg) - if reqd_tag > get_priority_by_name(wheel_abi.blacklist_tag): + if reqd_tag > wheel_policy.get_priority_by_name(wheel_abi.blacklist_tag): msg = ( 'cannot repair "%s" to "%s" ABI because it depends on ' "black-listed symbols." % (wheel_file, args.PLAT) @@ -158,7 +155,7 @@ def execute(args, p): abis = [policy["name"]] + policy["aliases"] if not args.ONLY_PLAT: - if reqd_tag < get_priority_by_name(wheel_abi.overall_tag): + if reqd_tag < wheel_policy.get_priority_by_name(wheel_abi.overall_tag): logger.info( ( "Wheel is eligible for a higher priority tag. " @@ -168,11 +165,12 @@ def execute(args, p): args.PLAT, wheel_abi.overall_tag, ) - higher_policy = get_policy_by_name(wheel_abi.overall_tag) + higher_policy = wheel_policy.get_policy_by_name(wheel_abi.overall_tag) abis = [higher_policy["name"]] + higher_policy["aliases"] + abis patcher = Patchelf() out_wheel = repair_wheel( + wheel_policy, wheel_file, abis=abis, lib_sdir=args.LIB_SDIR, diff --git a/src/auditwheel/main_show.py b/src/auditwheel/main_show.py index 28355738..a79ec446 100644 --- a/src/auditwheel/main_show.py +++ b/src/auditwheel/main_show.py @@ -2,6 +2,8 @@ import logging +from auditwheel.policy import WheelPolicies + logger = logging.getLogger(__name__) @@ -23,22 +25,17 @@ def execute(args, p): import json from os.path import basename, isfile - from .policy import ( - POLICY_PRIORITY_HIGHEST, - POLICY_PRIORITY_LOWEST, - get_policy_name, - get_priority_by_name, - load_policies, - ) from .wheel_abi import NonPlatformWheel, analyze_wheel_abi + wheel_policy = WheelPolicies.load() + fn = basename(args.WHEEL_FILE) if not isfile(args.WHEEL_FILE): p.error("cannot access %s. No such file" % args.WHEEL_FILE) try: - winfo = analyze_wheel_abi(args.WHEEL_FILE) + winfo = analyze_wheel_abi(wheel_policy, args.WHEEL_FILE) except NonPlatformWheel: logger.info(NonPlatformWheel.LOG_MESSAGE) return 1 @@ -52,7 +49,10 @@ def execute(args, p): % (fn, winfo.overall_tag) ) - if get_priority_by_name(winfo.pyfpe_tag) < POLICY_PRIORITY_HIGHEST: + if ( + wheel_policy.get_priority_by_name(winfo.pyfpe_tag) + < wheel_policy.priority_highest + ): printp( "This wheel uses the PyFPE_jbuf function, which is not compatible with the" " manylinux1 tag. (see https://www.python.org/dev/peps/pep-0513/" @@ -61,7 +61,7 @@ def execute(args, p): if args.verbose < 1: return - if get_priority_by_name(winfo.ucs_tag) < POLICY_PRIORITY_HIGHEST: + if wheel_policy.get_priority_by_name(winfo.ucs_tag) < wheel_policy.priority_highest: printp( "This wheel is compiled against a narrow unicode (UCS2) " "version of Python, which is not compatible with the " @@ -81,7 +81,7 @@ def execute(args, p): "system-provided shared libraries: %s" % ", ".join(libs_with_versions) ) - if get_priority_by_name(winfo.sym_tag) < POLICY_PRIORITY_HIGHEST: + if wheel_policy.get_priority_by_name(winfo.sym_tag) < wheel_policy.priority_highest: printp( ( 'This constrains the platform tag to "%s". ' @@ -95,15 +95,17 @@ def execute(args, p): if args.verbose < 1: return - libs = winfo.external_refs[get_policy_name(POLICY_PRIORITY_LOWEST)]["libs"] + libs = winfo.external_refs[ + wheel_policy.get_policy_name(wheel_policy.priority_lowest) + ]["libs"] if len(libs) == 0: printp("The wheel requires no external shared libraries! :)") else: printp("The following external shared libraries are required " "by the wheel:") print(json.dumps(dict(sorted(libs.items())), indent=4)) - for p in sorted(load_policies(), key=lambda p: p["priority"]): - if p["priority"] > get_priority_by_name(winfo.overall_tag): + for p in sorted(wheel_policy.policies, key=lambda p: p["priority"]): + if p["priority"] > wheel_policy.get_priority_by_name(winfo.overall_tag): libs = winfo.external_refs[p["name"]]["libs"] if len(libs): printp( diff --git a/src/auditwheel/policy/__init__.py b/src/auditwheel/policy/__init__.py index 97fd3888..4a566d78 100644 --- a/src/auditwheel/policy/__init__.py +++ b/src/auditwheel/policy/__init__.py @@ -18,6 +18,87 @@ # https://docs.python.org/3/library/platform.html#platform.architecture bits = 8 * (8 if sys.maxsize > 2**32 else 4) +_POLICY_JSON_MAP = { + Libc.GLIBC: _HERE / "manylinux-policy.json", + Libc.MUSL: _HERE / "musllinux-policy.json", +} + + +class WheelPolicies: + @staticmethod + def load(): + libc_variant = get_libc() + policies_path = _POLICY_JSON_MAP[libc_variant] + policy_dict = json.loads(policies_path.read_text()) + return WheelPolicies(policy_dict) + + def __init__(self, policies: dict) -> None: + self._policies = [] + self._musl_policy = _get_musl_policy() + self._arch_name = get_arch_name() + self._libc_variant = get_libc() + + _validate_pep600_compliance(policies) + for policy in policies: + if self._musl_policy is not None and policy["name"] not in { + "linux", + self._musl_policy, + }: + continue + if ( + self._arch_name in policy["symbol_versions"].keys() + or policy["name"] == "linux" + ): + if policy["name"] != "linux": + policy["symbol_versions"] = policy["symbol_versions"][ + self._arch_name + ] + policy["name"] = policy["name"] + "_" + self._arch_name + policy["aliases"] = [ + alias + "_" + self._arch_name for alias in policy["aliases"] + ] + policy["lib_whitelist"] = _fixup_musl_libc_soname( + policy["lib_whitelist"] + ) + self._policies.append(policy) + + if self._libc_variant == Libc.MUSL: + assert len(self._policies) == 2, self._policies + + @property + def policies(self): + return self._policies + + @property + def priority_highest(self): + return max(p["priority"] for p in self._policies) + + @property + def priority_lowest(self): + return min(p["priority"] for p in self._policies) + + def get_policy_by_name(self, name: str) -> dict | None: + matches = [ + p for p in self._policies if p["name"] == name or name in p["aliases"] + ] + if len(matches) == 0: + return None + if len(matches) > 1: + raise RuntimeError("Internal error. Policies should be unique") + return matches[0] + + def get_policy_name(self, priority: int) -> str | None: + matches = [p["name"] for p in self._policies if p["priority"] == priority] + if len(matches) == 0: + return None + if len(matches) > 1: + raise RuntimeError("Internal error. priorities should be unique") + return matches[0] + + def get_priority_by_name(self, name: str) -> int | None: + policy = self.get_policy_by_name(name) + return None if policy is None else policy["priority"] + def get_arch_name() -> str: machine = _platform_module.machine() @@ -65,12 +146,6 @@ def _validate_pep600_compliance(policies) -> None: symbol_versions[arch] = symbol_versions_arch -_POLICY_JSON_MAP = { - Libc.GLIBC: _HERE / "manylinux-policy.json", - Libc.MUSL: _HERE / "musllinux-policy.json", -} - - def _get_musl_policy(): if _LIBC != Libc.MUSL: return None @@ -78,9 +153,6 @@ def _get_musl_policy(): return f"musllinux_{musl_version.major}_{musl_version.minor}" -_MUSL_POLICY = _get_musl_policy() - - def _fixup_musl_libc_soname(whitelist): if _LIBC != Libc.MUSL: return whitelist @@ -105,60 +177,6 @@ def _fixup_musl_libc_soname(whitelist): return new_whitelist -with _POLICY_JSON_MAP[_LIBC].open() as f: - _POLICIES = [] - _policies_temp = json.load(f) - _validate_pep600_compliance(_policies_temp) - for _p in _policies_temp: - if _MUSL_POLICY is not None and _p["name"] not in {"linux", _MUSL_POLICY}: - continue - if _ARCH_NAME in _p["symbol_versions"].keys() or _p["name"] == "linux": - if _p["name"] != "linux": - _p["symbol_versions"] = _p["symbol_versions"][_ARCH_NAME] - _p["name"] = _p["name"] + "_" + _ARCH_NAME - _p["aliases"] = [alias + "_" + _ARCH_NAME for alias in _p["aliases"]] - _p["lib_whitelist"] = _fixup_musl_libc_soname(_p["lib_whitelist"]) - _POLICIES.append(_p) - if _LIBC == Libc.MUSL: - assert len(_POLICIES) == 2, _POLICIES - -POLICY_PRIORITY_HIGHEST = max(p["priority"] for p in _POLICIES) -POLICY_PRIORITY_LOWEST = min(p["priority"] for p in _POLICIES) - - -def load_policies(): - return _POLICIES - - -def _load_policy_schema(): - with open(join(dirname(abspath(__file__)), "policy-schema.json")) as f_: - schema = json.load(f_) - return schema - - -def get_policy_by_name(name: str) -> dict | None: - matches = [p for p in _POLICIES if p["name"] == name or name in p["aliases"]] - if len(matches) == 0: - return None - if len(matches) > 1: - raise RuntimeError("Internal error. Policies should be unique") - return matches[0] - - -def get_policy_name(priority: int) -> str | None: - matches = [p["name"] for p in _POLICIES if p["priority"] == priority] - if len(matches) == 0: - return None - if len(matches) > 1: - raise RuntimeError("Internal error. priorities should be unique") - return matches[0] - - -def get_priority_by_name(name: str) -> int | None: - policy = get_policy_by_name(name) - return None if policy is None else policy["priority"] - - def get_replace_platforms(name: str) -> list[str]: """Extract platform tag replacement rules from policy @@ -185,10 +203,14 @@ def get_replace_platforms(name: str) -> list[str]: from .external_references import lddtree_external_references # noqa from .versioned_symbols import versioned_symbols_policy # noqa +def _load_policy_schema(): + with open(join(dirname(abspath(__file__)), "policy-schema.json")) as f_: + schema = json.load(f_) + return schema + + __all__ = [ "lddtree_external_references", "versioned_symbols_policy", - "load_policies", - "POLICY_PRIORITY_HIGHEST", - "POLICY_PRIORITY_LOWEST", + "WheelPolicies", ] diff --git a/src/auditwheel/policy/external_references.py b/src/auditwheel/policy/external_references.py index 23afde85..2f833c2a 100644 --- a/src/auditwheel/policy/external_references.py +++ b/src/auditwheel/policy/external_references.py @@ -5,17 +5,15 @@ from typing import Any, Generator from ..elfutils import filter_undefined_symbols, is_subdir -from . import load_policies +from . import WheelPolicies log = logging.getLogger(__name__) LIBPYTHON_RE = re.compile(r"^libpython\d+\.\d+m?.so(.\d)*$") -def lddtree_external_references(lddtree: dict, wheel_path: str) -> dict: +def lddtree_external_references(wheel_policies: list, lddtree: dict, wheel_path: str) -> dict: # XXX: Document the lddtree structure, or put it in something # more stable than a big nested dict - policies = load_policies() - def filter_libs(libs: set[str], whitelist: set[str]) -> Generator[str, None, None]: for lib in libs: if "ld-linux" in lib or lib in ["ld64.so.2", "ld64.so.1"]: @@ -45,7 +43,7 @@ def get_req_external(libs: set[str], whitelist: set[str]) -> set[str]: return reqs ret: dict[str, dict[str, Any]] = {} - for p in policies: + for p in wheel_policies: needed_external_libs: set[str] = set() blacklist = {} diff --git a/src/auditwheel/policy/versioned_symbols.py b/src/auditwheel/policy/versioned_symbols.py index 7d4e0a18..ab90288f 100644 --- a/src/auditwheel/policy/versioned_symbols.py +++ b/src/auditwheel/policy/versioned_symbols.py @@ -2,12 +2,12 @@ import logging -from . import load_policies +from . import WheelPolicies log = logging.getLogger(__name__) -def versioned_symbols_policy(versioned_symbols: dict[str, set[str]]) -> int: +def versioned_symbols_policy(wheel_policy: WheelPolicies, versioned_symbols: dict[str, set[str]]) -> int: def policy_is_satisfied( policy_name: str, policy_sym_vers: dict[str, set[str]] ) -> bool: @@ -31,7 +31,7 @@ def policy_is_satisfied( sym_name, _, _ = symbol.partition("_") required_vers.setdefault(sym_name, set()).add(symbol) matching_policies: list[int] = [] - for p in load_policies(): + for p in wheel_policy.policies: policy_sym_vers = { sym_name: {sym_name + "_" + version for version in versions} for sym_name, versions in p["symbol_versions"].items() diff --git a/src/auditwheel/repair.py b/src/auditwheel/repair.py index 69e5df27..c172b471 100644 --- a/src/auditwheel/repair.py +++ b/src/auditwheel/repair.py @@ -16,7 +16,7 @@ from .elfutils import elf_read_dt_needed, elf_read_rpaths, is_subdir from .hashfile import hashfile -from .policy import get_replace_platforms +from .policy import WheelPolicies, get_replace_platforms from .wheel_abi import get_wheel_elfdata from .wheeltools import InWheelCtx, add_platforms @@ -32,6 +32,7 @@ def repair_wheel( + wheel_policy: WheelPolicies, wheel_path: str, abis: list[str], lib_sdir: str, @@ -41,7 +42,7 @@ def repair_wheel( exclude: list[str], strip: bool = False, ) -> str | None: - external_refs_by_fn = get_wheel_elfdata(wheel_path)[1] + external_refs_by_fn = get_wheel_elfdata(wheel_policy, wheel_path)[1] # Do not repair a pure wheel, i.e. has no external refs if not external_refs_by_fn: diff --git a/src/auditwheel/wheel_abi.py b/src/auditwheel/wheel_abi.py index ca72d04f..988e78b4 100644 --- a/src/auditwheel/wheel_abi.py +++ b/src/auditwheel/wheel_abi.py @@ -20,11 +20,8 @@ from .genericpkgctx import InGenericPkgCtx from .lddtree import lddtree from .policy import ( - POLICY_PRIORITY_HIGHEST, - POLICY_PRIORITY_LOWEST, - get_policy_name, + WheelPolicies, lddtree_external_references, - load_policies, versioned_symbols_policy, ) @@ -59,7 +56,7 @@ class NonPlatformWheel(WheelAbiError): @functools.lru_cache -def get_wheel_elfdata(wheel_fn: str): +def get_wheel_elfdata(wheel_policy: WheelPolicies, wheel_fn: str): full_elftree = {} nonpy_elftree = {} full_external_refs = {} @@ -105,7 +102,7 @@ def get_wheel_elfdata(wheel_fn: str): True for _ in elf_find_ucs2_symbols(elf) ) full_external_refs[fn] = lddtree_external_references( - elftree, ctx.path + wheel_policy.policies, elftree, ctx.path ) else: # If the ELF is not a Python extension, it might be @@ -148,7 +145,7 @@ def get_wheel_elfdata(wheel_fn: str): # should include it as an external reference, because # it might require additional external libraries. full_external_refs[fn] = lddtree_external_references( - nonpy_elftree[fn], ctx.path + wheel_policy.policies, nonpy_elftree[fn], ctx.path ) log.debug("full_elftree:\n%s", json.dumps(full_elftree, indent=4)) @@ -204,7 +201,7 @@ def get_versioned_symbols(libs): return result -def get_symbol_policies(versioned_symbols, external_versioned_symbols, external_refs): +def get_symbol_policies(wheel_policy, versioned_symbols, external_versioned_symbols, external_refs): """Get symbol policies Since white-list is different per policy, this function inspects versioned_symbol per policy when including external refs @@ -226,14 +223,14 @@ def get_symbol_policies(versioned_symbols, external_versioned_symbols, external_ ext_symbols = external_versioned_symbols[soname] for k in iter(ext_symbols): policy_symbols[k].update(ext_symbols[k]) - result.append((versioned_symbols_policy(policy_symbols), policy_symbols)) + result.append((versioned_symbols_policy(wheel_policy, policy_symbols), policy_symbols)) return result -def analyze_wheel_abi(wheel_fn: str) -> WheelAbIInfo: +def analyze_wheel_abi(wheel_policy: WheelPolicies, wheel_fn: str) -> WheelAbIInfo: external_refs = { p["name"]: {"libs": {}, "blacklist": {}, "priority": p["priority"]} - for p in load_policies() + for p in wheel_policy.policies } ( @@ -242,7 +239,7 @@ def analyze_wheel_abi(wheel_fn: str) -> WheelAbIInfo: versioned_symbols, has_ucs2, uses_PyFPE_jbuf, - ) = get_wheel_elfdata(wheel_fn) + ) = get_wheel_elfdata(wheel_policy, wheel_fn) for fn in elftree_by_fn.keys(): update(external_refs, external_refs_by_fn[fn]) @@ -253,9 +250,9 @@ def analyze_wheel_abi(wheel_fn: str) -> WheelAbIInfo: external_libs = get_external_libs(external_refs) external_versioned_symbols = get_versioned_symbols(external_libs) symbol_policies = get_symbol_policies( - versioned_symbols, external_versioned_symbols, external_refs + wheel_policy, versioned_symbols, external_versioned_symbols, external_refs ) - symbol_policy = versioned_symbols_policy(versioned_symbols) + symbol_policy = versioned_symbols_policy(wheel_policy, versioned_symbols) # let's keep the highest priority policy and # corresponding versioned_symbols @@ -265,30 +262,30 @@ def analyze_wheel_abi(wheel_fn: str) -> WheelAbIInfo: ref_policy = max( (e["priority"] for e in external_refs.values() if len(e["libs"]) == 0), - default=POLICY_PRIORITY_LOWEST, + default=wheel_policy.priority_lowest, ) blacklist_policy = max( (e["priority"] for e in external_refs.values() if len(e["blacklist"]) == 0), - default=POLICY_PRIORITY_LOWEST, + default=wheel_policy.priority_lowest, ) if has_ucs2: - ucs_policy = POLICY_PRIORITY_LOWEST + ucs_policy = wheel_policy.priority_lowest else: - ucs_policy = POLICY_PRIORITY_HIGHEST + ucs_policy = wheel_policy.priority_highest if uses_PyFPE_jbuf: - pyfpe_policy = POLICY_PRIORITY_LOWEST + pyfpe_policy = wheel_policy.priority_lowest else: - pyfpe_policy = POLICY_PRIORITY_HIGHEST - - ref_tag = get_policy_name(ref_policy) - sym_tag = get_policy_name(symbol_policy) - ucs_tag = get_policy_name(ucs_policy) - pyfpe_tag = get_policy_name(pyfpe_policy) - blacklist_tag = get_policy_name(blacklist_policy) - overall_tag = get_policy_name( + pyfpe_policy = wheel_policy.priority_highest + + ref_tag = wheel_policy.get_policy_name(ref_policy) + sym_tag = wheel_policy.get_policy_name(symbol_policy) + ucs_tag = wheel_policy.get_policy_name(ucs_policy) + pyfpe_tag = wheel_policy.get_policy_name(pyfpe_policy) + blacklist_tag = wheel_policy.get_policy_name(blacklist_policy) + overall_tag = wheel_policy.get_policy_name( min(symbol_policy, ref_policy, ucs_policy, pyfpe_policy, blacklist_policy) ) diff --git a/tests/integration/test_bundled_wheels.py b/tests/integration/test_bundled_wheels.py index fe51f5e6..bbed4881 100644 --- a/tests/integration/test_bundled_wheels.py +++ b/tests/integration/test_bundled_wheels.py @@ -13,6 +13,7 @@ import pytest from auditwheel import main_repair +from auditwheel.policy import WheelPolicies from auditwheel.wheel_abi import analyze_wheel_abi HERE = Path(__file__).parent.resolve() @@ -27,13 +28,17 @@ ], ) def test_analyze_wheel_abi(file, external_libs): - winfo = analyze_wheel_abi(str(HERE / file)) + wheel_policies = WheelPolicies.load() + winfo = analyze_wheel_abi(wheel_policies, str(HERE / file)) assert set(winfo.external_refs["manylinux_2_5_x86_64"]["libs"]) == external_libs @pytest.mark.skipif(platform.machine() != "x86_64", reason="only supported on x86_64") def test_analyze_wheel_abi_pyfpe(): - winfo = analyze_wheel_abi(str(HERE / "fpewheel-0.0.0-cp35-cp35m-linux_x86_64.whl")) + wheel_policies = WheelPolicies.load() + winfo = analyze_wheel_abi( + wheel_policies, str(HERE / "fpewheel-0.0.0-cp35-cp35m-linux_x86_64.whl") + ) assert ( winfo.sym_tag == "manylinux_2_5_x86_64" ) # for external symbols, it could get manylinux1 diff --git a/tests/integration/test_manylinux.py b/tests/integration/test_manylinux.py index b34ba532..b633f551 100644 --- a/tests/integration/test_manylinux.py +++ b/tests/integration/test_manylinux.py @@ -16,7 +16,7 @@ import pytest from elftools.elf.elffile import ELFFile -from auditwheel.policy import get_arch_name, get_priority_by_name +from auditwheel.policy import WheelPolicies, get_arch_name logger = logging.getLogger(__name__) @@ -859,11 +859,12 @@ def test_build_wheel_with_image_dependencies( "auditwheel -v repair --plat {policy} -w /io /io/{orig_wheel}" ) - policy_priority = get_priority_by_name(policy) + wheel_policy = WheelPolicies.load() + policy_priority = wheel_policy.get_priority_by_name(policy) older_policies = [ f"{p}_{PLATFORM}" for p in MANYLINUX_IMAGES.keys() - if policy_priority < get_priority_by_name(f"{p}_{PLATFORM}") + if policy_priority < wheel_policy.get_priority_by_name(f"{p}_{PLATFORM}") ] for target_policy in older_policies: # we shall fail to repair the wheel when targeting an older policy than diff --git a/tests/integration/test_policy_files.py b/tests/integration/test_policy_files.py index e92f7742..357cf8a1 100644 --- a/tests/integration/test_policy_files.py +++ b/tests/integration/test_policy_files.py @@ -3,26 +3,30 @@ from jsonschema import validate from auditwheel.policy import ( - POLICY_PRIORITY_HIGHEST, - POLICY_PRIORITY_LOWEST, + WheelPolicies, _load_policy_schema, - load_policies, versioned_symbols_policy, ) def test_policy(): - policy = load_policies() + wheel_policy = WheelPolicies.load() policy_schema = _load_policy_schema() - validate(policy, policy_schema) + validate(wheel_policy.policies, policy_schema) def test_policy_checks_glibc(): - policy = versioned_symbols_policy({"some_library.so": {"GLIBC_2.17"}}) - assert policy > POLICY_PRIORITY_LOWEST - policy = versioned_symbols_policy({"some_library.so": {"GLIBC_999"}}) - assert policy == POLICY_PRIORITY_LOWEST - policy = versioned_symbols_policy({"some_library.so": {"OPENSSL_1_1_0"}}) - assert policy == POLICY_PRIORITY_HIGHEST - policy = versioned_symbols_policy({"some_library.so": {"IAMALIBRARY"}}) - assert policy == POLICY_PRIORITY_HIGHEST + wheel_policy = WheelPolicies.load() + + policy = versioned_symbols_policy(wheel_policy, {"some_library.so": {"GLIBC_2.17"}}) + assert policy > wheel_policy.priority_lowest + policy = versioned_symbols_policy(wheel_policy, {"some_library.so": {"GLIBC_999"}}) + assert policy == wheel_policy.priority_lowest + policy = versioned_symbols_policy( + wheel_policy, {"some_library.so": {"OPENSSL_1_1_0"}} + ) + assert policy == wheel_policy.priority_highest + policy = versioned_symbols_policy( + wheel_policy, {"some_library.so": {"IAMALIBRARY"}} + ) + assert policy == wheel_policy.priority_highest diff --git a/tests/unit/test_policy.py b/tests/unit/test_policy.py index adbdad8c..5a5971bf 100644 --- a/tests/unit/test_policy.py +++ b/tests/unit/test_policy.py @@ -5,10 +5,9 @@ import pytest from auditwheel.policy import ( + WheelPolicies, _validate_pep600_compliance, get_arch_name, - get_policy_name, - get_priority_by_name, get_replace_platforms, lddtree_external_references, ) @@ -156,53 +155,53 @@ def test_pep600_compliance(): class TestPolicyAccess: def test_get_by_priority(self): _arch = get_arch_name() - assert get_policy_name(65) == f"manylinux_2_27_{_arch}" - assert get_policy_name(70) == f"manylinux_2_24_{_arch}" - assert get_policy_name(80) == f"manylinux_2_17_{_arch}" + wheel_policy = WheelPolicies.load() + assert wheel_policy.get_policy_name(65) == f"manylinux_2_27_{_arch}" + assert wheel_policy.get_policy_name(70) == f"manylinux_2_24_{_arch}" + assert wheel_policy.get_policy_name(80) == f"manylinux_2_17_{_arch}" if _arch in {"x86_64", "i686"}: - assert get_policy_name(90) == f"manylinux_2_12_{_arch}" - assert get_policy_name(100) == f"manylinux_2_5_{_arch}" - assert get_policy_name(0) == f"linux_{_arch}" + assert wheel_policy.get_policy_name(90) == f"manylinux_2_12_{_arch}" + assert wheel_policy.get_policy_name(100) == f"manylinux_2_5_{_arch}" + assert wheel_policy.get_policy_name(0) == f"linux_{_arch}" def test_get_by_priority_missing(self): - assert get_policy_name(101) is None + wheel_policy = WheelPolicies.load() + assert wheel_policy.get_policy_name(101) is None - @patch( - "auditwheel.policy._POLICIES", - [ + def test_get_by_priority_duplicate(self): + wheel_policy = WheelPolicies.load() + wheel_policy._policies = [ {"name": "duplicate", "priority": 0}, {"name": "duplicate", "priority": 0}, - ], - ) - def test_get_by_priority_duplicate(self): + ] with pytest.raises(RuntimeError): - get_policy_name(0) + wheel_policy.get_policy_name(0) def test_get_by_name(self): _arch = get_arch_name() - assert get_priority_by_name(f"manylinux_2_27_{_arch}") == 65 - assert get_priority_by_name(f"manylinux_2_24_{_arch}") == 70 - assert get_priority_by_name(f"manylinux2014_{_arch}") == 80 - assert get_priority_by_name(f"manylinux_2_17_{_arch}") == 80 + wheel_policy = WheelPolicies.load() + assert wheel_policy.get_priority_by_name(f"manylinux_2_27_{_arch}") == 65 + assert wheel_policy.get_priority_by_name(f"manylinux_2_24_{_arch}") == 70 + assert wheel_policy.get_priority_by_name(f"manylinux2014_{_arch}") == 80 + assert wheel_policy.get_priority_by_name(f"manylinux_2_17_{_arch}") == 80 if _arch in {"x86_64", "i686"}: - assert get_priority_by_name(f"manylinux2010_{_arch}") == 90 - assert get_priority_by_name(f"manylinux_2_12_{_arch}") == 90 - assert get_priority_by_name(f"manylinux1_{_arch}") == 100 - assert get_priority_by_name(f"manylinux_2_5_{_arch}") == 100 + assert wheel_policy.get_priority_by_name(f"manylinux2010_{_arch}") == 90 + assert wheel_policy.get_priority_by_name(f"manylinux_2_12_{_arch}") == 90 + assert wheel_policy.get_priority_by_name(f"manylinux1_{_arch}") == 100 + assert wheel_policy.get_priority_by_name(f"manylinux_2_5_{_arch}") == 100 def test_get_by_name_missing(self): - assert get_priority_by_name("nosuchpolicy") is None + wheel_policy = WheelPolicies.load() + assert wheel_policy.get_priority_by_name("nosuchpolicy") is None - @patch( - "auditwheel.policy._POLICIES", - [ + def test_get_by_name_duplicate(self): + wheel_policy = WheelPolicies.load() + wheel_policy._policies = [ {"name": "duplicate", "priority": 0}, {"name": "duplicate", "priority": 0}, - ], - ) - def test_get_by_name_duplicate(self): + ] with pytest.raises(RuntimeError): - get_priority_by_name("duplicate") + wheel_policy.get_priority_by_name("duplicate") class TestLddTreeExternalReferences: @@ -227,7 +226,10 @@ def test_filter_libs(self): "needed": libs, "libs": {lib: {"needed": [], "realpath": "/path/to/lib"} for lib in libs}, } - full_external_refs = lddtree_external_references(lddtree, "/path/to/wheel") + wheel_policy = WheelPolicies.load() + full_external_refs = lddtree_external_references( + wheel_policy.policies, lddtree, "/path/to/wheel" + ) # Assert that each policy only has the unfiltered libs. for policy in full_external_refs: diff --git a/tests/unit/test_wheel_abi.py b/tests/unit/test_wheel_abi.py index 882d3e15..640be560 100644 --- a/tests/unit/test_wheel_abi.py +++ b/tests/unit/test_wheel_abi.py @@ -6,6 +6,7 @@ import pytest from auditwheel import wheel_abi +from auditwheel.policy import WheelPolicies class TestGetWheelElfdata: @@ -44,8 +45,9 @@ def test_finds_shared_library_in_purelib(self, filenames, message, monkeypatch): "elf_file_filter", lambda fns: [(fn, pretend.stub()) for fn in fns], ) + wheel_policy = WheelPolicies.load() with pytest.raises(RuntimeError) as exec_info: - wheel_abi.get_wheel_elfdata("/fakepath") + wheel_abi.get_wheel_elfdata(wheel_policy, "/fakepath") assert exec_info.value.args == (message,)