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

Retain executable mode #2046

Merged
merged 4 commits into from Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2041.bugfix.rst
@@ -0,0 +1 @@
Preserve file modes during pkg files copying, but clear read only flag for target afterwards.
9 changes: 7 additions & 2 deletions setuptools/command/build_py.py
Expand Up @@ -7,6 +7,7 @@
import io
import distutils.errors
import itertools
import stat

from setuptools.extern import six
from setuptools.extern.six.moves import map, filter, filterfalse
Expand All @@ -20,6 +21,10 @@ def run_2to3(self, files, doctests=True):
"do nothing"


def make_writable(target):
os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE)


class build_py(orig.build_py, Mixin2to3):
"""Enhanced 'build_py' command that includes data files with packages

Expand Down Expand Up @@ -120,8 +125,8 @@ def build_package_data(self):
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
srcfile = os.path.join(src_dir, filename)
outf, copied = self.copy_file(
srcfile, target, preserve_mode=False)
outf, copied = self.copy_file(srcfile, target)
make_writable(target)
srcfile = os.path.abspath(srcfile)
if (copied and
srcfile in self.distribution.convert_2to3_doctests):
Expand Down
41 changes: 38 additions & 3 deletions setuptools/tests/test_build_py.py
Expand Up @@ -2,6 +2,8 @@
import stat
import shutil

import pytest

from setuptools.dist import Distribution


Expand All @@ -26,9 +28,10 @@ def test_directories_in_package_data_glob(tmpdir_cwd):

def test_read_only(tmpdir_cwd):
"""
Ensure mode is not preserved in copy for package modules
and package data, as that causes problems
with deleting read-only files on Windows.
Ensure read-only flag is not preserved in copy
for package modules and package data, as that
causes problems with deleting read-only files on
Windows.

#1451
"""
Expand All @@ -47,3 +50,35 @@ def test_read_only(tmpdir_cwd):
dist.parse_command_line()
dist.run_commands()
shutil.rmtree('build')


@pytest.mark.xfail(
'platform.system() == "Windows"',
reason="On Windows, files do not have executable bits",
raises=AssertionError,
strict=True,
)
def test_executable_data(tmpdir_cwd):
"""
Ensure executable bit is preserved in copy for
package data, as users rely on it for scripts.

#2041
"""
dist = Distribution(dict(
script_name='setup.py',
script_args=['build_py'],
packages=['pkg'],
package_data={'pkg': ['run-me']},
name='pkg',
))
os.makedirs('pkg')
open('pkg/__init__.py', 'w').close()
open('pkg/run-me', 'w').close()
os.chmod('pkg/run-me', 0o700)

dist.parse_command_line()
dist.run_commands()

assert os.stat('build/lib/pkg/run-me').st_mode & stat.S_IEXEC, \
"Script is not executable"