Author gward
Recipients gward, serhiy.storchaka, vstinner
Date 2014-11-21.01:37:29
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1416533851.02.0.242709851165.issue21775@psf.upfronthosting.co.za>
In-reply-to
Content
> Would it be possible to write a unit test, maybe using unittest.mock to 
> mock most parts?

Good idea! Turns out this was quite straightforward. The test patch is:

--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -1,6 +1,7 @@
 # Copyright (C) 2003 Python Software Foundation
 
 import unittest
+import unittest.mock
 import shutil
 import tempfile
 import sys
@@ -758,6 +759,20 @@
         self.assertEqual(os.stat(restrictive_subdir).st_mode,
                           os.stat(restrictive_subdir_dst).st_mode)
 
+    @unittest.mock.patch('os.chmod')
+    def test_copytree_winerror(self, mock_patch):
+        # When copying to VFAT, copystat() raises OSError. On Windows, the
+        # exception object has a meaningful 'winerror' attribute, but not
+        # on other operating systems. Do not assume 'winerror' is set.
+        src_dir = tempfile.mkdtemp()
+        dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
+        self.addCleanup(shutil.rmtree, src_dir)
+        self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
+
+        mock_patch.side_effect = PermissionError('ka-boom')
+        with self.assertRaises(shutil.Error):
+            shutil.copytree(src_dir, dst_dir)
+
     @unittest.skipIf(os.name == 'nt', 'temporarily disabled on Windows')
     @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link')
     def test_dont_copy_file_onto_link_to_itself(self):


When run without the bug fix, this reproduces my original failure nicely:

$ ./python Lib/test/test_shutil.py
................s........................s.s........E..s..............................s..
======================================================================
ERROR: test_copytree_winerror (__main__.TestShutil)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/data/src/cpython/3.4/Lib/shutil.py", line 337, in copytree
    copystat(src, dst)
  File "/data/src/cpython/3.4/Lib/shutil.py", line 191, in copystat
    lookup("chmod")(dst, mode, follow_symlinks=follow)
  File "/data/src/cpython/3.4/Lib/unittest/mock.py", line 896, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/data/src/cpython/3.4/Lib/unittest/mock.py", line 952, in _mock_call
    raise effect
PermissionError: ka-boom

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/data/src/cpython/3.4/Lib/unittest/mock.py", line 1136, in patched
    return func(*args, **keywargs)
  File "Lib/test/test_shutil.py", line 774, in test_copytree_winerror
    shutil.copytree(src_dir, dst_dir)
  File "/data/src/cpython/3.4/Lib/shutil.py", line 340, in copytree
    if why.winerror is None:
AttributeError: 'PermissionError' object has no attribute 'winerror'

----------------------------------------------------------------------
Ran 89 tests in 0.095s

FAILED (errors=1, skipped=5)

Excellent! No need for root privs, and the bug is quite obvious.

Apply my getattr() fix, and the test passes.

I'll commit on branch 3.4 and merge to default.
History
Date User Action Args
2014-11-21 01:37:31gwardsetrecipients: + gward, vstinner, serhiy.storchaka
2014-11-21 01:37:31gwardsetmessageid: <1416533851.02.0.242709851165.issue21775@psf.upfronthosting.co.za>
2014-11-21 01:37:30gwardlinkissue21775 messages
2014-11-21 01:37:29gwardcreate