diff options
author | Harmen Stoppels <harmenstoppels@gmail.com> | 2022-03-23 10:31:23 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-23 10:31:23 +0100 |
commit | 773da7ceba116154e999187e0d9bcef5d58bebd4 (patch) | |
tree | e749f12fd66fb9b0279863455a97c1e17434588b /lib | |
parent | 487b1c369098c050aa04f8fd99bf3a6e578847c1 (diff) | |
download | spack-773da7ceba116154e999187e0d9bcef5d58bebd4.tar.gz spack-773da7ceba116154e999187e0d9bcef5d58bebd4.tar.bz2 spack-773da7ceba116154e999187e0d9bcef5d58bebd4.tar.xz spack-773da7ceba116154e999187e0d9bcef5d58bebd4.zip |
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.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/llnl/util/filesystem.py | 23 | ||||
-rw-r--r-- | lib/spack/spack/build_systems/python.py | 4 | ||||
-rw-r--r-- | lib/spack/spack/test/llnl/util/filesystem.py | 24 |
3 files changed, 49 insertions, 2 deletions
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py index 22ca97c347..179e1703b9 100644 --- a/lib/spack/llnl/util/filesystem.py +++ b/lib/spack/llnl/util/filesystem.py @@ -617,6 +617,29 @@ def get_filetype(path_name): 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)) def chgrp_if_not_world_writable(path, group): """chgrp path to group if path is not world writable""" diff --git a/lib/spack/spack/build_systems/python.py b/lib/spack/spack/build_systems/python.py index b3552f0337..6aa225ba6c 100644 --- a/lib/spack/spack/build_systems/python.py +++ b/lib/spack/spack/build_systems/python.py @@ -11,7 +11,7 @@ import llnl.util.tty as tty from llnl.util.filesystem import ( filter_file, find, - get_filetype, + is_nonsymlink_exe_with_shebang, path_contains_subdirectory, same_path, working_dir, @@ -230,7 +230,7 @@ class PythonPackage(PackageBase): view.link(src, dst) elif not os.path.islink(src): 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: filter_file( python_prefix, os.path.abspath( diff --git a/lib/spack/spack/test/llnl/util/filesystem.py b/lib/spack/spack/test/llnl/util/filesystem.py index 2bd6994754..b5a4594efb 100644 --- a/lib/spack/spack/test/llnl/util/filesystem.py +++ b/lib/spack/spack/test/llnl/util/filesystem.py @@ -674,3 +674,27 @@ def test_temporary_dir_context_manager(): with fs.temporary_dir() as tmp_dir: assert previous_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') |