diff options
author | Larry Hastings <larry@hastings.org> | 2012-07-15 00:55:11 (GMT) |
---|---|---|
committer | Larry Hastings <larry@hastings.org> | 2012-07-15 00:55:11 (GMT) |
commit | ad5ae0456e64036d1f39a15c214c2f394a8fb9c3 (patch) | |
tree | 379c65057bb04c36b9509b44d0a5c9db68758827 /Lib | |
parent | 7c6309c6afebecd9cefe7c547b87cf23abcbe2a3 (diff) | |
download | cpython-ad5ae0456e64036d1f39a15c214c2f394a8fb9c3.zip cpython-ad5ae0456e64036d1f39a15c214c2f394a8fb9c3.tar.gz cpython-ad5ae0456e64036d1f39a15c214c2f394a8fb9c3.tar.bz2 |
- Issue #15238: shutil.copystat now copies Linux "extended attributes".
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/shutil.py | 44 | ||||
-rw-r--r-- | Lib/test/test_shutil.py | 10 |
2 files changed, 32 insertions, 22 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py index 6d80fee..2b2a9be5 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -132,6 +132,27 @@ def copymode(src, dst, symlinks=False): st = stat_func(src) chmod_func(dst, stat.S_IMODE(st.st_mode)) +if hasattr(os, 'listxattr'): + def _copyxattr(src, dst, symlinks=False): + """Copy extended filesystem attributes from `src` to `dst`. + + Overwrite existing attributes. + + If the optional flag `symlinks` is set, symlinks won't be followed. + + """ + + for name in os.listxattr(src, follow_symlinks=symlinks): + try: + value = os.getxattr(src, name, follow_symlinks=symlinks) + os.setxattr(dst, name, value, follow_symlinks=symlinks) + except OSError as e: + if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA): + raise +else: + def _copyxattr(*args, **kwargs): + pass + def copystat(src, dst, symlinks=False): """Copy all stat info (mode bits, atime, mtime, flags) from src to dst. @@ -184,27 +205,7 @@ def copystat(src, dst, symlinks=False): break else: raise - -if hasattr(os, 'listxattr'): - def _copyxattr(src, dst, symlinks=False): - """Copy extended filesystem attributes from `src` to `dst`. - - Overwrite existing attributes. - - If the optional flag `symlinks` is set, symlinks won't be followed. - - """ - - for name in os.listxattr(src, follow_symlinks=symlinks): - try: - value = os.getxattr(src, name, follow_symlinks=symlinks) - os.setxattr(dst, name, value, follow_symlinks=symlinks) - except OSError as e: - if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA): - raise -else: - def _copyxattr(*args, **kwargs): - pass + _copyxattr(src, dst, symlinks=follow) def copy(src, dst, symlinks=False): """Copy data and mode bits ("cp src dst"). Return the file's destination. @@ -235,7 +236,6 @@ def copy2(src, dst, symlinks=False): dst = os.path.join(dst, os.path.basename(src)) copyfile(src, dst, symlinks=symlinks) copystat(src, dst, symlinks=symlinks) - _copyxattr(src, dst, symlinks=symlinks) return dst def ignore_patterns(*patterns): diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index a2b6e88..cbbc36f 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -410,6 +410,16 @@ class TestShutil(unittest.TestCase): finally: os.setxattr = orig_setxattr + # test that shutil.copystat copies xattrs + src = os.path.join(tmp_dir, 'the_original') + write_file(src, src) + os.setxattr(src, 'user.the_value', b'fiddly') + dst = os.path.join(tmp_dir, 'the_copy') + write_file(dst, dst) + shutil.copystat(src, dst) + self.assertEqual(os.listxattr(src), ['user.the_value']) + self.assertEqual(os.getxattr(src, 'user.the_value'), b'fiddly') + @support.skip_unless_symlink @support.skip_unless_xattr @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0, |