Skip to content

Commit

Permalink
Use assumed ABI3 version for shared objects without ".abi3." (#119)
Browse files Browse the repository at this point in the history
Co-authored-by: William Woodruff <[email protected]>
  • Loading branch information
vadz and woodruffw authored Nov 12, 2024
1 parent efab2b9 commit be052e5
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 13 deletions.
3 changes: 1 addition & 2 deletions abi3audit/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ def main() -> None:
parser.add_argument(
"--assume-minimum-abi3",
action=_PyVersionAction,
default=PyVersion(3, 2),
help="assumed abi3 version (3.x, with x>=2) if it cannot be detected",
)
args = parser.parse_args()
Expand All @@ -231,7 +230,7 @@ def main() -> None:
specs = []
for spec in args.specs:
try:
specs.extend(make_specs(spec))
specs.extend(make_specs(spec, assume_minimum_abi3=args.assume_minimum_abi3))
except InvalidSpec as e:
console.log(f"[red]:thumbs_down: processing error: {e}")
sys.exit(1)
Expand Down
18 changes: 12 additions & 6 deletions abi3audit/_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from typing import Union
from zipfile import ZipFile

from abi3info.models import PyVersion
from packaging import utils
from packaging.tags import Tag

Expand Down Expand Up @@ -89,7 +90,7 @@ def _extractor(self) -> Extractor:
Spec = Union[WheelSpec, SharedObjectSpec, PyPISpec]


def make_specs(val: str) -> list[Spec]:
def make_specs(val: str, assume_minimum_abi3: PyVersion | None = None) -> list[Spec]:
"""
Constructs a (minimally) valid list of `Spec` instances from the given input.
"""
Expand All @@ -101,15 +102,20 @@ def make_specs(val: str) -> list[Spec]:
return [WheelSpec(val)]
elif any(val.endswith(suf) for suf in _SHARED_OBJECT_SUFFIXES):
# NOTE: We allow untagged shared objects when they're indirectly
# audited (e.g. via an abi3 wheel), but not directly (since
# without a tag here we don't know if it's abi3 at all).
if ".abi3." not in val:
raise InvalidSpec(f"'{val}' must contain '.abi3.' to be recognized as a shared object")
# audited (e.g. via an abi3 wheel), but when auditing them directly we
# only allow them if we have a minimum abi3 version to check against.
if assume_minimum_abi3 is None and ".abi3." not in val:
raise InvalidSpec(
"--assume-minimum-abi3 must be used when extension "
"does not contain '.abi3.' infix"
)
return [SharedObjectSpec(val)]
elif re.match(_DISTRIBUTION_NAME_RE, val, re.IGNORECASE):
return [PyPISpec(val)]
else:
raise InvalidSpec(f"'{val}' does not look like a valid spec")
raise InvalidSpec(
f"'{val}' does not look like a valid wheel, shared object, or package name"
)


class ExtractorError(ValueError):
Expand Down
10 changes: 7 additions & 3 deletions abi3audit/_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, extractor: extract.SharedObjectExtractor):
self._extractor = extractor
self.path = self._extractor.path

def abi3_version(self, assume_lowest: PyVersion = PyVersion(3, 2)) -> PyVersion | None:
def abi3_version(self, assume_lowest: PyVersion) -> PyVersion | None:
# If we're dealing with a shared object that was extracted from a wheel,
# we try and suss out the abi3 version from the wheel's own tags.
if self._extractor.parent is not None:
Expand All @@ -50,14 +50,18 @@ def abi3_version(self, assume_lowest: PyVersion = PyVersion(3, 2)) -> PyVersion
# filename. This doesn't tell us anything about the specific abi3 version, so
# we assume the lowest.
if ".abi3" in self._extractor.path.suffixes:
if assume_lowest is None:
assume_lowest = PyVersion(3, 2)

logger.debug(
"no wheel to infer abi3 version from; assuming (%s)",
assume_lowest,
)
return assume_lowest

# With no wheel tags and no filename tag, we have nothing to go on.
return None
# With no wheel tags and no filename tag, fall back on the assumed ABI
# version (which is possibly None).
return assume_lowest

def __str__(self) -> str:
parents = []
Expand Down
8 changes: 6 additions & 2 deletions test/test_extract.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from abi3info.models import PyVersion

from abi3audit._extract import (
ExtractorError,
Expand All @@ -16,10 +17,13 @@ def test_make_spec():
assert make_specs("foo.abi3.so") == [SharedObjectSpec("foo.abi3.so")]
assert make_specs("foo") == [PyPISpec("foo")]

# Shared objects need to be tagged with `.abi3`.
with pytest.raises(InvalidSpec, match="'foo.so' must contain '.abi3.'"):
# Shared objects need to be tagged with `.abi3` or --assume-minimum-abi3
# must be used.
with pytest.raises(InvalidSpec, match="--assume-minimum-abi3"):
make_specs("foo.so")

make_specs("foo.so", assume_minimum_abi3=PyVersion(3, 2)) == [SharedObjectSpec("foo.so")]

# Anything that doesn't look like a wheel, shared object, or PyPI package fails.
with pytest.raises(InvalidSpec):
make_specs("foo@foo")
Expand Down

0 comments on commit be052e5

Please sign in to comment.