Skip to content

Commit

Permalink
feature: replace unzip usage with Python's zipfile
Browse files Browse the repository at this point in the history
This removes the need to have `unzip` installed.

fixes pypa#258
closes pypa#312
  • Loading branch information
mayeut committed Aug 24, 2021
1 parent 24fddf5 commit ab6d7ff
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 19 deletions.
3 changes: 1 addition & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ Installation
$ pip3 install auditwheel
It requires Python 3.6+, and runs on Linux. It requires that the shell command
``unzip`` be available in the ``PATH``. Only systems that use `ELF
It requires Python 3.6+, and runs on Linux. Only systems that use `ELF
<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>`_-based linkage
are supported (this should be essentially every Linux).

Expand Down
28 changes: 12 additions & 16 deletions auditwheel/tools.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import argparse
import os
import shutil
from glob import glob
from os.path import join as pjoin
from typing import Any, Iterable, List
import zipfile
import subprocess
Expand Down Expand Up @@ -38,19 +35,18 @@ def zip2dir(zip_fname: str, out_dir: str) -> None:
out_dir : str
Directory path containing files to go in the zip archive
"""
# Use unzip command rather than zipfile module to preserve permissions
# http://bugs.python.org/issue15795
subprocess.check_output(['unzip', '-o', '-d', out_dir, zip_fname])

try:
# but sometimes preserving permssions is really bad, and makes it
# we don't have the permissions to read any of the files
with open(glob(pjoin(out_dir, '*.dist-info/RECORD'))[0]):
pass
except PermissionError:
shutil.rmtree(out_dir)
with zipfile.ZipFile(zip_fname) as zf:
zf.extractall(out_dir)
with zipfile.ZipFile(zip_fname, "r") as z:
for name in z.namelist():
member = z.getinfo(name)
extracted_path = z.extract(member, out_dir)
attr = member.external_attr >> 16
if member.is_dir():
# this is always rebuilt as 755 by dir2zip
os.chmod(extracted_path, 0o755)
elif attr != 0:
attr &= 511 # only keep permission bits
attr |= 6 << 6 # at least read/write for current user
os.chmod(extracted_path, attr)


def dir2zip(in_dir: str, zip_fname: str) -> None:
Expand Down
Binary file added tests/unit/test-permissions.zip.xz
Binary file not shown.
45 changes: 44 additions & 1 deletion tests/unit/test_tools.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import argparse
import lzma

from pathlib import Path

import pytest
from auditwheel.tools import EnvironmentDefault

from auditwheel.tools import EnvironmentDefault, zip2dir, dir2zip


@pytest.mark.parametrize(
Expand Down Expand Up @@ -43,3 +48,41 @@ def test_environment_action_invalid_env(monkeypatch):
dest='PLAT',
choices=choices,
default='manylinux1')


def _write_test_permissions_zip(path):
source_zip_xz = Path(__file__).parent / "test-permissions.zip.xz"
with lzma.open(source_zip_xz) as f:
path.write_bytes(f.read())


def _check_permissions(path):
for i in range(8):
for j in range(8):
for k in range(8):
mode = (path / f"{i}{j}{k}.f").stat().st_mode
assert ((mode >> 6) & 7) == (i | 6) # always read/write
assert ((mode >> 3) & 7) == j
assert ((mode >> 0) & 7) == k
mode = (path / f"{i}{j}{k}.d").stat().st_mode
assert ((mode >> 6) & 7) == 7 # always read/write/execute
assert ((mode >> 3) & 7) == 5 # always read/execute
assert ((mode >> 0) & 7) == 5 # always read/execute


def test_zip2dir_permissions(tmp_path):
source_zip = tmp_path / "test-permissions.zip"
_write_test_permissions_zip(source_zip)
extract_path = tmp_path / "unzip"
zip2dir(str(source_zip), str(extract_path))
_check_permissions(extract_path)


def test_zip2dir_round_trip_permissions(tmp_path):
source_zip = tmp_path / "test-permissions.zip"
_write_test_permissions_zip(source_zip)
extract_path = tmp_path / "unzip2"
zip2dir(str(source_zip), str(tmp_path / "unzip1"))
dir2zip(str(tmp_path / "unzip1"), str(tmp_path / "tmp.zip"))
zip2dir(str(tmp_path / "tmp.zip"), str(extract_path))
_check_permissions(extract_path)

0 comments on commit ab6d7ff

Please sign in to comment.