else:
return
else:
- stat_func, chmod_func = _stat, os.chmod
+ stat_func = _stat
+ if os.name == 'nt' and os.path.islink(dst):
+ def chmod_func(*args):
+ os.chmod(*args, follow_symlinks=True)
+ else:
+ chmod_func = os.chmod
st = stat_func(src)
chmod_func(dst, stat.S_IMODE(st.st_mode))
shutil.copymode(src, dst)
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
# On Windows, os.chmod does not follow symlinks (issue #15411)
- if os.name != 'nt':
- # follow src link
- os.chmod(dst, stat.S_IRWXO)
- shutil.copymode(src_link, dst)
- self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
- # follow dst link
- os.chmod(dst, stat.S_IRWXO)
- shutil.copymode(src, dst_link)
- self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
- # follow both links
- os.chmod(dst, stat.S_IRWXO)
- shutil.copymode(src_link, dst_link)
- self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
+ # follow src link
+ os.chmod(dst, stat.S_IRWXO)
+ shutil.copymode(src_link, dst)
+ self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
+ # follow dst link
+ os.chmod(dst, stat.S_IRWXO)
+ shutil.copymode(src, dst_link)
+ self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
+ # follow both links
+ os.chmod(dst, stat.S_IRWXO)
+ shutil.copymode(src_link, dst_link)
+ self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
@unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
@os_helper.skip_unless_symlink
--- /dev/null
+Fix :func:`shutil.copymode` and :func:`shutil.copystat` on Windows.
+Previously they worked differenly if *dst* is a symbolic link:
+they modified the permission bits of *dst* itself
+rather than the file it points to if *follow_symlinks* is true or *src* is
+not a symbolic link, and did not modify the permission bits if
+*follow_symlinks* is false and *src* is a symbolic link.