python: drop dependency on file
for script check (#29513)
`file` was used to detect Python scripts with shebangs, so that the interpreter could be changed from <python prefix> to <view path>. With this change, we detect shebangs using Python instead, so that `file` is no longer required.
This commit is contained in:
parent
487b1c3690
commit
773da7ceba
@ -617,6 +617,29 @@ def get_filetype(path_name):
|
|||||||
return output.strip()
|
return output.strip()
|
||||||
|
|
||||||
|
|
||||||
|
@system_path_filter
|
||||||
|
def is_nonsymlink_exe_with_shebang(path):
|
||||||
|
"""
|
||||||
|
Returns whether the path is an executable script with a shebang.
|
||||||
|
Return False when the path is a *symlink* to an executable script.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
st = os.lstat(path)
|
||||||
|
# Should not be a symlink
|
||||||
|
if stat.S_ISLNK(st.st_mode):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Should be executable
|
||||||
|
if not st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Should start with a shebang
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
return f.read(2) == b'#!'
|
||||||
|
except (IOError, OSError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@system_path_filter(arg_slice=slice(1))
|
@system_path_filter(arg_slice=slice(1))
|
||||||
def chgrp_if_not_world_writable(path, group):
|
def chgrp_if_not_world_writable(path, group):
|
||||||
"""chgrp path to group if path is not world writable"""
|
"""chgrp path to group if path is not world writable"""
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
from llnl.util.filesystem import (
|
from llnl.util.filesystem import (
|
||||||
filter_file,
|
filter_file,
|
||||||
find,
|
find,
|
||||||
get_filetype,
|
is_nonsymlink_exe_with_shebang,
|
||||||
path_contains_subdirectory,
|
path_contains_subdirectory,
|
||||||
same_path,
|
same_path,
|
||||||
working_dir,
|
working_dir,
|
||||||
@ -230,7 +230,7 @@ def add_files_to_view(self, view, merge_map):
|
|||||||
view.link(src, dst)
|
view.link(src, dst)
|
||||||
elif not os.path.islink(src):
|
elif not os.path.islink(src):
|
||||||
shutil.copy2(src, dst)
|
shutil.copy2(src, dst)
|
||||||
is_script = 'script' in get_filetype(src)
|
is_script = is_nonsymlink_exe_with_shebang(src)
|
||||||
if is_script and not python_is_external:
|
if is_script and not python_is_external:
|
||||||
filter_file(
|
filter_file(
|
||||||
python_prefix, os.path.abspath(
|
python_prefix, os.path.abspath(
|
||||||
|
@ -674,3 +674,27 @@ def test_temporary_dir_context_manager():
|
|||||||
with fs.temporary_dir() as tmp_dir:
|
with fs.temporary_dir() as tmp_dir:
|
||||||
assert previous_dir != os.path.realpath(os.getcwd())
|
assert previous_dir != os.path.realpath(os.getcwd())
|
||||||
assert os.path.realpath(str(tmp_dir)) == os.path.realpath(os.getcwd())
|
assert os.path.realpath(str(tmp_dir)) == os.path.realpath(os.getcwd())
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.platform == 'win32', reason="No shebang on Windows")
|
||||||
|
def test_is_nonsymlink_exe_with_shebang(tmpdir):
|
||||||
|
with tmpdir.as_cwd():
|
||||||
|
# Create an executable with shebang.
|
||||||
|
with open('executable_script', 'wb') as f:
|
||||||
|
f.write(b'#!/interpreter')
|
||||||
|
os.chmod('executable_script', 0o100775)
|
||||||
|
|
||||||
|
with open('executable_but_not_script', 'wb') as f:
|
||||||
|
f.write(b'#/not-a-shebang')
|
||||||
|
os.chmod('executable_but_not_script', 0o100775)
|
||||||
|
|
||||||
|
with open('not_executable_with_shebang', 'wb') as f:
|
||||||
|
f.write(b'#!/interpreter')
|
||||||
|
os.chmod('not_executable_with_shebang', 0o100664)
|
||||||
|
|
||||||
|
os.symlink('executable_script', 'symlink_to_executable_script')
|
||||||
|
|
||||||
|
assert fs.is_nonsymlink_exe_with_shebang('executable_script')
|
||||||
|
assert not fs.is_nonsymlink_exe_with_shebang('executable_but_not_script')
|
||||||
|
assert not fs.is_nonsymlink_exe_with_shebang('not_executable_with_shebang')
|
||||||
|
assert not fs.is_nonsymlink_exe_with_shebang('symlink_to_executable_script')
|
||||||
|
@ -13,7 +13,11 @@
|
|||||||
from shutil import copy
|
from shutil import copy
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.filesystem import copy_tree, get_filetype, path_contains_subdirectory
|
from llnl.util.filesystem import (
|
||||||
|
copy_tree,
|
||||||
|
is_nonsymlink_exe_with_shebang,
|
||||||
|
path_contains_subdirectory,
|
||||||
|
)
|
||||||
from llnl.util.lang import match_predicate
|
from llnl.util.lang import match_predicate
|
||||||
|
|
||||||
from spack import *
|
from spack import *
|
||||||
@ -1369,7 +1373,7 @@ def add_files_to_view(self, view, merge_map):
|
|||||||
view.link(src, dst, spec=self.spec)
|
view.link(src, dst, spec=self.spec)
|
||||||
elif not os.path.islink(src):
|
elif not os.path.islink(src):
|
||||||
copy(src, dst)
|
copy(src, dst)
|
||||||
if 'script' in get_filetype(src):
|
if is_nonsymlink_exe_with_shebang(src):
|
||||||
filter_file(
|
filter_file(
|
||||||
self.spec.prefix,
|
self.spec.prefix,
|
||||||
os.path.abspath(
|
os.path.abspath(
|
||||||
|
Loading…
Reference in New Issue
Block a user