[Python-checkins] cpython: - Issue #15238: shutil.copystat now copies Linux "extended attributes".

larry.hastings python-checkins at python.org
Sun Jul 15 02:55:26 CEST 2012


http://hg.python.org/cpython/rev/5f62c317c202
changeset:   78101:5f62c317c202
user:        Larry Hastings <larry at hastings.org>
date:        Sat Jul 14 17:55:11 2012 -0700
summary:
  - Issue #15238: shutil.copystat now copies Linux "extended attributes".

files:
  Doc/library/shutil.rst  |   5 +-
  Lib/shutil.py           |  44 ++++++++++++++--------------
  Lib/test/test_shutil.py |  10 ++++++
  Misc/NEWS               |   2 +
  4 files changed, 37 insertions(+), 24 deletions(-)


diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -86,10 +86,11 @@
    from *src* to *dst*.  The file contents, owner, and group are unaffected.  *src*
    and *dst* are path names given as strings.  If *src* and *dst* are both
    symbolic links and *symlinks* true, the stats of the link will be copied as
-   far as the platform allows.
+   far as the platform allows.  On Linux, :func:`copystat` also copies the
+   "extended attributes" where possible.
 
    .. versionchanged:: 3.3
-      Added *symlinks* argument.
+      Added *symlinks* argument and support for Linux extended attributes.
 
 .. function:: copy(src, dst, symlinks=False)
 
diff --git a/Lib/shutil.py b/Lib/shutil.py
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -132,6 +132,27 @@
     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 @@
                     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 @@
         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
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -410,6 +410,16 @@
         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,
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -35,6 +35,8 @@
 Library
 -------
 
+- Issue #15238: shutil.copystat now copies Linux "extended attributes".
+
 - Issue #15230: runpy.run_path now correctly sets __package__ as described
   in the documentation
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list