Skip to content

Commit

Permalink
Merge pull request #1 from boegel/read-only-copy2
Browse files Browse the repository at this point in the history
implement test for `copy_file` to copy read-only file with extended attributes
  • Loading branch information
jfgrimm authored Sep 18, 2024
2 parents 150d7c3 + 23b5b78 commit 747eddd
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 4 deletions.
8 changes: 4 additions & 4 deletions easybuild/tools/filetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2443,11 +2443,11 @@ def copy_file(path, target_path, force_in_dry_run=False):
# see https://bugs.python.org/issue24538
shutil.copy2(path, target_path)
_log.info("%s copied to %s", path, target_path)
# catch the more general OSError instead of PermissionError,
# since Python 2.7 doesn't support PermissionError
except OSError as err:
# catch the more general OSError instead of PermissionError, since python2 doesn't support
# PermissionError
if not os.stat(path).st_mode & stat.S_IWUSR:
# failure not due to read-only file
# if file is writable (not read-only), then we give up since it's not a simple permission error
if os.path.exists(target_path) and os.stat(target_path).st_mode & stat.S_IWUSR:
raise EasyBuildError("Failed to copy file %s to %s: %s", path, target_path, err)

pyver = LooseVersion(platform.python_version())
Expand Down
44 changes: 44 additions & 0 deletions test/framework/filetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
@author: Maxime Boissonneault (Compute Canada, Universite Laval)
"""
import datetime
import filecmp
import glob
import logging
import os
Expand All @@ -51,6 +52,8 @@
from easybuild.tools.config import IGNORE, ERROR, build_option, update_build_option
from easybuild.tools.multidiff import multidiff
from easybuild.tools.py2vs3 import StringIO, std_urllib
from easybuild.tools.run import run_cmd
from easybuild.tools.systemtools import LINUX, get_os_type


class FileToolsTest(EnhancedTestCase):
Expand Down Expand Up @@ -1912,6 +1915,47 @@ def test_copy_file(self):
# However, if we add 'force_in_dry_run=True' it should throw an exception
self.assertErrorRegex(EasyBuildError, "Could not copy *", ft.copy_file, src, target, force_in_dry_run=True)

def test_copy_file_xattr(self):
"""Test copying a file with extended attributes using copy_file."""
# test copying a read-only files with extended attributes set
# first, create a special file with extended attributes
special_file = os.path.join(self.test_prefix, 'special.txt')
ft.write_file(special_file, 'special')
# make read-only, and set extended attributes
attr = ft.which('attr')
xattr = ft.which('xattr')
# try to attr (Linux) or xattr (macOS) to set extended attributes foo=bar
cmd = None
if attr:
cmd = "attr -s foo -V bar %s" % special_file
elif xattr:
cmd = "xattr -w foo bar %s" % special_file

if cmd:
(_, ec) = run_cmd(cmd, simple=False, log_all=False, log_ok=False)

# need to make file read-only after setting extended attribute
ft.adjust_permissions(special_file, stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH, add=False)

# only proceed if setting extended attribute worked
if ec == 0:
target = os.path.join(self.test_prefix, 'copy.txt')
ft.copy_file(special_file, target)
self.assertTrue(os.path.exists(target))
self.assertTrue(filecmp.cmp(special_file, target, shallow=False))

# only verify wheter extended attributes were also copied on Linux,
# since shutil.copy2 doesn't copy them on macOS;
# see warning at https://docs.python.org/3/library/shutil.html
if get_os_type() == LINUX:
if attr:
cmd = "attr -g foo %s" % target
else:
cmd = "xattr -l %s" % target
(out, ec) = run_cmd(cmd, simple=False, log_all=False, log_ok=False)
self.assertEqual(ec, 0)
self.assertTrue(out.endswith('\nbar\n'))

def test_copy_files(self):
"""Test copy_files function."""
test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
Expand Down

0 comments on commit 747eddd

Please sign in to comment.