Skip to content

Commit

Permalink
Fix cirular import in policy submodule
Browse files Browse the repository at this point in the history
The `external_versioned_symbols` and `versioned_symbols_policy`
functions used to live in two separate files, importing code from the
`__init__.py` file in the same module. As this file itself imports both
of these symbols, this creates a cirtular import. We can easily break
this by moving the two functions into the same `__init__.py` file.
  • Loading branch information
lkollar committed Nov 7, 2023
1 parent c33af5f commit 2d4b03b
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 133 deletions.
117 changes: 115 additions & 2 deletions src/auditwheel/policy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
import json
import logging
import platform as _platform_module
import re
import sys
from collections import defaultdict
from os.path import abspath, dirname, join
from pathlib import Path
from typing import Any, Generator

from auditwheel.elfutils import filter_undefined_symbols, is_subdir

from ..libc import Libc, get_libc
from ..musllinux import find_musl_libc, get_musl_version
from .external_references import lddtree_external_references
from .versioned_symbols import versioned_symbols_policy

_HERE = Path(__file__).parent
LIBPYTHON_RE = re.compile(r"^libpython\d+\.\d+m?.so(.\d)*$")

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -207,6 +210,116 @@ def _load_policy_schema():
return schema


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:
policy_satisfied = True
for name in set(required_vers) & set(policy_sym_vers):
if not required_vers[name].issubset(policy_sym_vers[name]):
for symbol in required_vers[name] - policy_sym_vers[name]:
logger.debug(
"Package requires %s, incompatible with "
"policy %s which requires %s",
symbol,
policy_name,
policy_sym_vers[name],
)
policy_satisfied = False
return policy_satisfied

required_vers: dict[str, set[str]] = {}
for symbols in versioned_symbols.values():
for symbol in symbols:
sym_name, _, _ = symbol.partition("_")
required_vers.setdefault(sym_name, set()).add(symbol)
matching_policies: list[int] = []
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()
}
if policy_is_satisfied(p["name"], policy_sym_vers):
matching_policies.append(p["priority"])

if len(matching_policies) == 0:
# the base policy (generic linux) should always match
raise RuntimeError("Internal error")

return max(matching_policies)


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
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"]:
# always exclude ELF dynamic linker/loader
# 'ld64.so.2' on s390x
# 'ld64.so.1' on ppc64le
# 'ld-linux*' on other platforms
continue
if LIBPYTHON_RE.match(lib):
# always exclude libpythonXY
continue
if lib in whitelist:
# exclude any libs in the whitelist
continue
yield lib

def get_req_external(libs: set[str], whitelist: set[str]) -> set[str]:
# get all the required external libraries
libs = libs.copy()
reqs = set()
while libs:
lib = libs.pop()
reqs.add(lib)
for dep in filter_libs(lddtree["libs"][lib]["needed"], whitelist):
if dep not in reqs:
libs.add(dep)
return reqs

ret: dict[str, dict[str, Any]] = {}
for p in wheel_policies:
needed_external_libs: set[str] = set()
blacklist = {}

if not (p["name"] == "linux" and p["priority"] == 0):
# special-case the generic linux platform here, because it
# doesn't have a whitelist. or, you could say its
# whitelist is the complete set of all libraries. so nothing
# is considered "external" that needs to be copied in.
whitelist = set(p["lib_whitelist"])
blacklist_libs = set(p["blacklist"].keys()) & set(lddtree["needed"])
blacklist = {k: p["blacklist"][k] for k in blacklist_libs}
blacklist = filter_undefined_symbols(lddtree["realpath"], blacklist)
needed_external_libs = get_req_external(
set(filter_libs(lddtree["needed"], whitelist)), whitelist
)

pol_ext_deps = {}
for lib in needed_external_libs:
if is_subdir(lddtree["libs"][lib]["realpath"], wheel_path):
# we didn't filter libs that resolved via RPATH out
# earlier because we wanted to make sure to pick up
# our elf's indirect dependencies. But now we want to
# filter these ones out, since they're not "external".
logger.debug("RPATH FTW: %s", lib)
continue
pol_ext_deps[lib] = lddtree["libs"][lib]["realpath"]
ret[p["name"]] = {
"libs": pol_ext_deps,
"priority": p["priority"],
"blacklist": blacklist,
}
return ret


__all__ = [
"lddtree_external_references",
"versioned_symbols_policy",
Expand Down
78 changes: 0 additions & 78 deletions src/auditwheel/policy/external_references.py

This file was deleted.

46 changes: 0 additions & 46 deletions src/auditwheel/policy/versioned_symbols.py

This file was deleted.

14 changes: 7 additions & 7 deletions src/auditwheel/wheel_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@
)
from .genericpkgctx import InGenericPkgCtx
from .lddtree import lddtree
from .policy import (
WheelPolicies,
lddtree_external_references,
versioned_symbols_policy,
)
from .policy import WheelPolicies, lddtree_external_references, versioned_symbols_policy

log = logging.getLogger(__name__)
WheelAbIInfo = namedtuple(
Expand Down Expand Up @@ -201,7 +197,9 @@ def get_versioned_symbols(libs):
return result


def get_symbol_policies(wheel_policy, 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
Expand All @@ -223,7 +221,9 @@ def get_symbol_policies(wheel_policy, versioned_symbols, external_versioned_symb
ext_symbols = external_versioned_symbols[soname]
for k in iter(ext_symbols):
policy_symbols[k].update(ext_symbols[k])
result.append((versioned_symbols_policy(wheel_policy, policy_symbols), policy_symbols))
result.append(
(versioned_symbols_policy(wheel_policy, policy_symbols), policy_symbols)
)
return result


Expand Down

0 comments on commit 2d4b03b

Please sign in to comment.