Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/auditwheel/main_repair.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import argparse
import logging
import zlib
from pathlib import Path

from auditwheel.patcher import Patchelf

from . import tools
from .policy import WheelPolicies
from .tools import EnvironmentDefault

Expand Down Expand Up @@ -40,6 +42,18 @@ def configure_parser(sub_parsers) -> None: # type: ignore[no-untyped-def]
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("WHEEL_FILE", type=Path, help="Path to wheel file.", nargs="+")
parser.add_argument(
"-z",
"--zip-level",
action=EnvironmentDefault,
metavar="zip",
env="AUDITWHEEL_ZIP_LEVEL",
dest="zip",
type=int,
help="Compress level to be used to create zip file.",
choices=list(range(zlib.Z_NO_COMPRESSION, zlib.Z_BEST_COMPRESSION + 1)),
default=zlib.Z_DEFAULT_COMPRESSION,
)
parser.add_argument(
"--plat",
action=EnvironmentDefault,
Expand Down Expand Up @@ -119,6 +133,7 @@ def execute(args: argparse.Namespace, parser: argparse.ArgumentParser) -> int:
wheel_dir: Path = args.WHEEL_DIR.absolute()
wheel_files: list[Path] = args.WHEEL_FILE
wheel_policy = WheelPolicies()
tools._COMPRESS_LEVEL = args.zip

for wheel_file in wheel_files:
if not wheel_file.is_file():
Expand Down
24 changes: 23 additions & 1 deletion src/auditwheel/tools.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
from __future__ import annotations

import argparse
import logging
import os
import subprocess
import zipfile
import zlib
from collections.abc import Generator, Iterable
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, TypeVar

_T = TypeVar("_T")

logger = logging.getLogger(__name__)

# Default: zlib.Z_DEFAULT_COMPRESSION (-1 aka. level 6) balances speed and size.
# Maintained for typical builds where iteration speed outweighs distribution savings.
# Override via AUDITWHEEL_ZIP_LEVEL/--zip-level for:
# - some test builds that needs no compression at all (0)
# - bandwidth-constrained or large amount of downloads (9)
_COMPRESS_LEVEL = zlib.Z_DEFAULT_COMPRESSION


def unique_by_index(sequence: Iterable[_T]) -> list[_T]:
"""unique elements in `sequence` in the order in which they occur
Expand Down Expand Up @@ -90,6 +101,7 @@ def zip2dir(zip_fname: Path, out_dir: Path) -> None:
out_dir : str
Directory path containing files to go in the zip archive
"""
start = datetime.now()
with zipfile.ZipFile(zip_fname, "r") as z:
for name in z.namelist():
member = z.getinfo(name)
Expand All @@ -102,6 +114,9 @@ def zip2dir(zip_fname: Path, out_dir: Path) -> None:
attr &= 511 # only keep permission bits
attr |= 6 << 6 # at least read/write for current user
os.chmod(extracted_path, attr)
logger.debug(
"zip2dir from %s to %s takes %s", zip_fname, out_dir, datetime.now() - start
)


def dir2zip(in_dir: Path, zip_fname: Path, date_time: datetime | None = None) -> None:
Expand All @@ -120,6 +135,7 @@ def dir2zip(in_dir: Path, zip_fname: Path, date_time: datetime | None = None) ->
date_time : Optional[datetime]
Time stamp to set on each file in the archive
"""
start = datetime.now()
in_dir = in_dir.resolve(strict=True)
if date_time is None:
st = in_dir.stat()
Expand All @@ -140,7 +156,10 @@ def dir2zip(in_dir: Path, zip_fname: Path, date_time: datetime | None = None) ->
zinfo.date_time = date_time_args
zinfo.compress_type = compression
with open(fname, "rb") as fp:
z.writestr(zinfo, fp.read())
z.writestr(zinfo, fp.read(), compresslevel=_COMPRESS_LEVEL)
logger.debug(
"dir2zip from %s to %s takes %s", in_dir, zip_fname, datetime.now() - start
)


def tarbz2todir(tarbz2_fname: Path, out_dir: Path) -> None:
Expand All @@ -157,11 +176,14 @@ def __init__(
required: bool = True,
default: str | None = None,
choices: Iterable[str] | None = None,
type: type | None = None,
**kwargs: Any,
) -> None:
self.env_default = os.environ.get(env)
self.env = env
if self.env_default:
if type:
self.env_default = type(self.env_default)
default = self.env_default
if default:
required = False
Expand Down
1 change: 1 addition & 0 deletions tests/integration/test_bundled_wheels.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def test_wheel_source_date_epoch(tmp_path, monkeypatch):
func=Mock(),
prog="auditwheel",
verbose=1,
zip=None,
)
monkeypatch.setenv("SOURCE_DATE_EPOCH", "650203200")
# patchelf might not be available as we aren't running in a manylinux container
Expand Down