mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-113188: Fix shutil.copymode() on Windows (GH-113189)
Previously it worked differently if dst is a symbolic link: it 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 nothing if follow_symlinks is false and src is a symbolic link. Also document similar changes in shutil.copystat().
This commit is contained in:
parent
bdc8d667ab
commit
6e02d79f96
3 changed files with 24 additions and 14 deletions
|
@ -306,7 +306,12 @@ def copymode(src, dst, *, follow_symlinks=True):
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
else:
|
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)
|
st = stat_func(src)
|
||||||
chmod_func(dst, stat.S_IMODE(st.st_mode))
|
chmod_func(dst, stat.S_IMODE(st.st_mode))
|
||||||
|
|
|
@ -1101,19 +1101,18 @@ class TestCopy(BaseTest, unittest.TestCase):
|
||||||
shutil.copymode(src, dst)
|
shutil.copymode(src, dst)
|
||||||
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
||||||
# On Windows, os.chmod does not follow symlinks (issue #15411)
|
# On Windows, os.chmod does not follow symlinks (issue #15411)
|
||||||
if os.name != 'nt':
|
# follow src link
|
||||||
# follow src link
|
os.chmod(dst, stat.S_IRWXO)
|
||||||
os.chmod(dst, stat.S_IRWXO)
|
shutil.copymode(src_link, dst)
|
||||||
shutil.copymode(src_link, dst)
|
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
||||||
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
# follow dst link
|
||||||
# follow dst link
|
os.chmod(dst, stat.S_IRWXO)
|
||||||
os.chmod(dst, stat.S_IRWXO)
|
shutil.copymode(src, dst_link)
|
||||||
shutil.copymode(src, dst_link)
|
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
||||||
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
# follow both links
|
||||||
# follow both links
|
os.chmod(dst, stat.S_IRWXO)
|
||||||
os.chmod(dst, stat.S_IRWXO)
|
shutil.copymode(src_link, dst_link)
|
||||||
shutil.copymode(src_link, dst_link)
|
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
||||||
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
|
@unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
|
||||||
@os_helper.skip_unless_symlink
|
@os_helper.skip_unless_symlink
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
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.
|
Loading…
Add table
Add a link
Reference in a new issue