Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] wheel install_as_egg does not honor file mode #3167

Merged
merged 9 commits into from Apr 4, 2022
50 changes: 29 additions & 21 deletions setuptools/archive_util.py
Expand Up @@ -100,29 +100,37 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
raise UnrecognizedFormat("%s is not a zip file" % (filename,))

with zipfile.ZipFile(filename) as z:
for info in z.infolist():
name = info.filename
_unpack_zipfile_obj(z, extract_dir, progress_filter)

# don't extract absolute paths or ones with .. in them
if name.startswith('/') or '..' in name.split('/'):
continue

target = os.path.join(extract_dir, *name.split('/'))
target = progress_filter(name, target)
if not target:
continue
if name.endswith('/'):
# directory
ensure_directory(target)
else:
# file
ensure_directory(target)
data = z.read(info.filename)
with open(target, 'wb') as f:
f.write(data)
unix_attributes = info.external_attr >> 16
if unix_attributes:
os.chmod(target, unix_attributes)
def _unpack_zipfile_obj(zipfile_obj, extract_dir, progress_filter=default_filter):
"""Internal/private API used by other parts of setuptools.
Similar to ``unpack_zipfile``, but receives an already opened :obj:`zipfile.ZipFile`
object instead of a filename.
"""
for info in zipfile_obj.infolist():
name = info.filename

# don't extract absolute paths or ones with .. in them
if name.startswith('/') or '..' in name.split('/'):
continue

target = os.path.join(extract_dir, *name.split('/'))
target = progress_filter(name, target)
if not target:
continue
if name.endswith('/'):
# directory
ensure_directory(target)
else:
# file
ensure_directory(target)
data = zipfile_obj.read(info.filename)
with open(target, 'wb') as f:
f.write(data)
unix_attributes = info.external_attr >> 16
if unix_attributes:
os.chmod(target, unix_attributes)


def _resolve_tar_file_or_dir(tar_obj, tar_member_obj):
Expand Down
87 changes: 87 additions & 0 deletions setuptools/tests/test_wheel.py
Expand Up @@ -6,6 +6,8 @@
from distutils.sysconfig import get_config_var
from distutils.util import get_platform
import contextlib
import pathlib
import stat
import glob
import inspect
import os
Expand Down Expand Up @@ -614,3 +616,88 @@ def sys_tags():
monkeypatch.setattr('setuptools.wheel.sys_tags', sys_tags)
assert Wheel(
'onnxruntime-0.1.2-cp36-cp36m-manylinux1_x86_64.whl').is_compatible()


def test_wheel_mode():
@contextlib.contextmanager
def build_wheel(extra_file_defs=None, **kwargs):
file_defs = {
'setup.py': (DALS(
'''
# -*- coding: utf-8 -*-
from setuptools import setup
import setuptools
setup(**%r)
'''
) % kwargs).encode('utf-8'),
}
if extra_file_defs:
file_defs.update(extra_file_defs)
with tempdir() as source_dir:
path.build(file_defs, source_dir)
runsh = pathlib.Path(source_dir) / "script.sh"
os.chmod(runsh, 0o777)
subprocess.check_call((sys.executable, 'setup.py',
'-q', 'bdist_wheel'), cwd=source_dir)
yield glob.glob(os.path.join(source_dir, 'dist', '*.whl'))[0]

params = dict(
id='script',
file_defs={
'script.py': DALS(
'''
#/usr/bin/python
print('hello world!')
'''
),
'script.sh': DALS(
'''
#/bin/sh
echo 'hello world!'
'''
),
},
setup_kwargs=dict(
scripts=['script.py', 'script.sh'],
),
install_tree=flatten_tree({
'foo-1.0-py{py_version}.egg': {
'EGG-INFO': [
'PKG-INFO',
'RECORD',
'WHEEL',
'top_level.txt',
{'scripts': [
'script.py',
'script.sh'
]}

]
}
})
)

project_name = params.get('name', 'foo')
version = params.get('version', '1.0')
install_tree = params.get('install_tree')
file_defs = params.get('file_defs', {})
setup_kwargs = params.get('setup_kwargs', {})

with build_wheel(
name=project_name,
version=version,
install_requires=[],
extras_require={},
extra_file_defs=file_defs,
**setup_kwargs
) as filename, tempdir() as install_dir:
_check_wheel_install(filename, install_dir,
install_tree, project_name,
version, None)
w = Wheel(filename)
base = pathlib.Path(install_dir) / w.egg_name()
script_sh = base / "EGG-INFO" / "scripts" / "script.sh"
assert script_sh.exists()
if sys.platform != 'win32':
# Editable file mode has no effect on Windows
assert oct(stat.S_IMODE(script_sh.stat().st_mode)) == "0o777"
4 changes: 2 additions & 2 deletions setuptools/wheel.py
Expand Up @@ -15,6 +15,7 @@
from setuptools.extern.packaging.tags import sys_tags
from setuptools.extern.packaging.utils import canonicalize_name
from setuptools.command.egg_info import write_requirements
from setuptools.archive_util import _unpack_zipfile_obj


WHEEL_NAME = re.compile(
Expand Down Expand Up @@ -121,8 +122,7 @@ def get_metadata(name):
raise ValueError(
'unsupported wheel format version: %s' % wheel_version)
# Extract to target directory.
os.mkdir(destination_eggdir)
zf.extractall(destination_eggdir)
_unpack_zipfile_obj(zf, destination_eggdir)
# Convert metadata.
dist_info = os.path.join(destination_eggdir, dist_info)
dist = pkg_resources.Distribution.from_location(
Expand Down