Skip to content

Commit

Permalink
Fix pex file looses the executable permissions of binary files (#703)
Browse files Browse the repository at this point in the history
Fixes #223
  • Loading branch information
fhoering authored and jsirois committed Apr 5, 2019
1 parent b2beec7 commit a4c5d96
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 8 deletions.
11 changes: 4 additions & 7 deletions pex/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@

import contextlib
import os
import shutil
import tempfile
import uuid
from hashlib import sha1
from site import makepath
from threading import Lock

from pex.common import rename_if_empty, safe_mkdir, safe_mkdtemp, safe_open
from pex.common import rename_if_empty, safe_mkdir, safe_mkdtemp
from pex.compatibility import exec_function
from pex.finders import register_finders
from pex.third_party.pkg_resources import (
Expand Down Expand Up @@ -176,11 +175,9 @@ def cache_distribution(cls, zf, source, target_dir):
target_dir_tmp = target_dir + '.' + uuid.uuid4().hex
for name in zf.namelist():
if name.startswith(source) and not name.endswith('/'):
# strip off prefix + '/'
target_name = os.path.join(dependency_basename, name[len(source) + 1:])
with contextlib.closing(zf.open(name)) as zi:
with safe_open(os.path.join(target_dir_tmp, target_name), 'wb') as fp:
shutil.copyfileobj(zi, fp)
zf.extract(name, target_dir_tmp)
os.rename(os.path.join(target_dir_tmp, source),
os.path.join(target_dir_tmp, dependency_basename))

rename_if_empty(target_dir_tmp, target_dir)

Expand Down
55 changes: 54 additions & 1 deletion tests/test_pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
named_temporary_file,
run_simple_pex_test,
temporary_dir,
write_simple_pex
write_simple_pex,
safe_mkdir,
temporary_content,
run_simple_pex
)
from pex.util import DistributionHelper

Expand Down Expand Up @@ -236,6 +239,56 @@ def test_pex_run():
assert fake_stdout.read() == b'hellohello'


def test_pex_executable():
# Tests that pex keeps executable permissions
with temporary_dir() as temp_dir:
pex_dir = os.path.join(temp_dir, 'pex_dir')
safe_mkdir(pex_dir)

with open(os.path.join(pex_dir, 'exe.py'), 'w') as fp:
fp.write(textwrap.dedent('''
import subprocess
import os
import sys
import my_package
path = os.path.join(os.path.dirname(my_package.__file__), 'bin/start.sh')
sys.stdout.write(subprocess.check_output([path]).decode('utf-8'))
'''))

project_content = {
'setup.py': textwrap.dedent('''
from setuptools import setup
setup(
name='my_project',
version='0.0.0.0',
zip_safe=True,
packages=['my_package'],
package_data={'my_package': ['bin/*']},
install_requires=[],
)
'''),
'my_package/__init__.py': 0,
'my_package/bin/start.sh': ("#!/usr/bin/env bash\n"
"echo 'hello world from start.sh!'"),
'my_package/my_module.py': 'def do_something():\n print("hello world!")\n',
}
pex_builder = PEXBuilder(path=pex_dir)
with temporary_content(project_content, perms=0o755) as project_dir:
installer = WheelInstaller(project_dir)
bdist = DistributionHelper.distribution_from_path(installer.bdist())
pex_builder.add_dist_location(bdist.location)
pex_builder.set_executable(os.path.join(pex_dir, 'exe.py'))
pex_builder.freeze()

app_pex = os.path.join(os.path.join(temp_dir, 'out_pex_dir'), 'app.pex')
pex_builder.build(app_pex)
std_out, rc = run_simple_pex(app_pex,
env={'PEX_ROOT': os.path.join(temp_dir, '.pex')})
assert rc == 0
assert std_out.decode('utf-8') == 'hello world from start.sh!\n'


def test_pex_paths():
# Tests that PEX_PATH allows importing sources from the referenced pex.
with named_temporary_file() as fake_stdout:
Expand Down

0 comments on commit a4c5d96

Please sign in to comment.