[Python-checkins] r72178 - in python/trunk: Lib/shutil.py Lib/test/test_shutil.py Misc/NEWS

antoine.pitrou python-checkins at python.org
Fri May 1 22:55:36 CEST 2009


Author: antoine.pitrou
Date: Fri May  1 22:55:35 2009
New Revision: 72178

Log:
Issue #3002: `shutil.copyfile()` and `shutil.copytree()` now raise an
error when a named pipe is encountered, rather than blocking infinitely.



Modified:
   python/trunk/Lib/shutil.py
   python/trunk/Lib/test/test_shutil.py
   python/trunk/Misc/NEWS

Modified: python/trunk/Lib/shutil.py
==============================================================================
--- python/trunk/Lib/shutil.py	(original)
+++ python/trunk/Lib/shutil.py	Fri May  1 22:55:35 2009
@@ -11,11 +11,15 @@
 import fnmatch
 
 __all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
-           "copytree","move","rmtree","Error"]
+           "copytree","move","rmtree","Error", "SpecialFileError"]
 
 class Error(EnvironmentError):
     pass
 
+class SpecialFileError(EnvironmentError):
+    """Raised when trying to do a kind of operation (e.g. copying) which is
+    not supported on a special file (e.g. a named pipe)"""
+
 try:
     WindowsError
 except NameError:
@@ -48,6 +52,15 @@
 
     fsrc = None
     fdst = None
+    for fn in [src, dst]:
+        try:
+            st = os.stat(fn)
+        except OSError:
+            # File most likely does not exist
+            pass
+        # XXX What about other special files? (sockets, devices...)
+        if stat.S_ISFIFO(st.st_mode):
+            raise SpecialFileError("`%s` is a named pipe" % fn)
     try:
         fsrc = open(src, 'rb')
         fdst = open(dst, 'wb')
@@ -157,14 +170,14 @@
             elif os.path.isdir(srcname):
                 copytree(srcname, dstname, symlinks, ignore)
             else:
+                # Will raise a SpecialFileError for unsupported file types
                 copy2(srcname, dstname)
-            # XXX What about devices, sockets etc.?
-        except (IOError, os.error), why:
-            errors.append((srcname, dstname, str(why)))
         # catch the Error from the recursive copytree so that we can
         # continue with other files
         except Error, err:
             errors.extend(err.args[0])
+        except EnvironmentError, why:
+            errors.append((srcname, dstname, str(why)))
     try:
         copystat(src, dst)
     except OSError, why:

Modified: python/trunk/Lib/test/test_shutil.py
==============================================================================
--- python/trunk/Lib/test/test_shutil.py	(original)
+++ python/trunk/Lib/test/test_shutil.py	Fri May  1 22:55:35 2009
@@ -9,6 +9,7 @@
 import os.path
 from test import test_support
 from test.test_support import TESTFN
+TESTFN2 = TESTFN + "2"
 
 class TestShutil(unittest.TestCase):
     def test_rmtree_errors(self):
@@ -240,6 +241,38 @@
             finally:
                 shutil.rmtree(TESTFN, ignore_errors=True)
 
+    if hasattr(os, "mkfifo"):
+        # Issue #3002: copyfile and copytree block indefinitely on named pipes
+        def test_copyfile_named_pipe(self):
+            os.mkfifo(TESTFN)
+            try:
+                self.assertRaises(shutil.SpecialFileError,
+                                  shutil.copyfile, TESTFN, TESTFN2)
+                self.assertRaises(shutil.SpecialFileError,
+                                  shutil.copyfile, __file__, TESTFN)
+            finally:
+                os.remove(TESTFN)
+
+        def test_copytree_named_pipe(self):
+            os.mkdir(TESTFN)
+            try:
+                subdir = os.path.join(TESTFN, "subdir")
+                os.mkdir(subdir)
+                pipe = os.path.join(subdir, "mypipe")
+                os.mkfifo(pipe)
+                try:
+                    shutil.copytree(TESTFN, TESTFN2)
+                except shutil.Error as e:
+                    errors = e.args[0]
+                    self.assertEqual(len(errors), 1)
+                    src, dst, error_msg = errors[0]
+                    self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
+                else:
+                    self.fail("shutil.Error should have been raised")
+            finally:
+                shutil.rmtree(TESTFN, ignore_errors=True)
+                shutil.rmtree(TESTFN2, ignore_errors=True)
+
 
 class TestMove(unittest.TestCase):
 

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Fri May  1 22:55:35 2009
@@ -261,6 +261,9 @@
 Library
 -------
 
+- Issue #3002: ``shutil.copyfile()`` and ``shutil.copytree()`` now raise an
+  error when a named pipe is encountered, rather than blocking infinitely.
+
 - Issue #3959: The ipaddr module has been added to the standard library.
   Contributed by Google.
 


More information about the Python-checkins mailing list