mirror of https://github.com/python/cpython
bpo-26791: Update shutil.move() to provide the same symlink move behavior as the mv shell when moving a symlink into a directory that is the target of the symlink (GH-21759)
This commit is contained in:
parent
1b19d73768
commit
c66b577d9f
|
@ -885,7 +885,7 @@ def move(src, dst, copy_function=copy2):
|
||||||
sys.audit("shutil.move", src, dst)
|
sys.audit("shutil.move", src, dst)
|
||||||
real_dst = dst
|
real_dst = dst
|
||||||
if os.path.isdir(dst):
|
if os.path.isdir(dst):
|
||||||
if _samefile(src, dst):
|
if _samefile(src, dst) and not os.path.islink(src):
|
||||||
# We might be on a case insensitive filesystem,
|
# We might be on a case insensitive filesystem,
|
||||||
# perform the rename anyway.
|
# perform the rename anyway.
|
||||||
os.rename(src, dst)
|
os.rename(src, dst)
|
||||||
|
|
|
@ -2688,6 +2688,35 @@ class TestMove(BaseTest, unittest.TestCase):
|
||||||
finally:
|
finally:
|
||||||
os.rmdir(dst_dir)
|
os.rmdir(dst_dir)
|
||||||
|
|
||||||
|
# bpo-26791: Check that a symlink to a directory can
|
||||||
|
# be moved into that directory.
|
||||||
|
@mock_rename
|
||||||
|
def _test_move_symlink_to_dir_into_dir(self, dst):
|
||||||
|
src = os.path.join(self.src_dir, 'linktodir')
|
||||||
|
dst_link = os.path.join(self.dst_dir, 'linktodir')
|
||||||
|
os.symlink(self.dst_dir, src, target_is_directory=True)
|
||||||
|
shutil.move(src, dst)
|
||||||
|
self.assertTrue(os.path.islink(dst_link))
|
||||||
|
self.assertTrue(os.path.samefile(self.dst_dir, dst_link))
|
||||||
|
self.assertFalse(os.path.exists(src))
|
||||||
|
|
||||||
|
# Repeat the move operation with the destination
|
||||||
|
# symlink already in place (should raise shutil.Error).
|
||||||
|
os.symlink(self.dst_dir, src, target_is_directory=True)
|
||||||
|
with self.assertRaises(shutil.Error):
|
||||||
|
shutil.move(src, dst)
|
||||||
|
self.assertTrue(os.path.samefile(self.dst_dir, dst_link))
|
||||||
|
self.assertTrue(os.path.exists(src))
|
||||||
|
|
||||||
|
@os_helper.skip_unless_symlink
|
||||||
|
def test_move_symlink_to_dir_into_dir(self):
|
||||||
|
self._test_move_symlink_to_dir_into_dir(self.dst_dir)
|
||||||
|
|
||||||
|
@os_helper.skip_unless_symlink
|
||||||
|
def test_move_symlink_to_dir_into_symlink_to_dir(self):
|
||||||
|
dst = os.path.join(self.src_dir, 'otherlinktodir')
|
||||||
|
os.symlink(self.dst_dir, dst, target_is_directory=True)
|
||||||
|
self._test_move_symlink_to_dir_into_dir(dst)
|
||||||
|
|
||||||
@os_helper.skip_unless_dac_override
|
@os_helper.skip_unless_dac_override
|
||||||
@unittest.skipUnless(hasattr(os, 'lchflags')
|
@unittest.skipUnless(hasattr(os, 'lchflags')
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
:func:`shutil.move` now moves a symlink into a directory when that
|
||||||
|
directory is the target of the symlink. This provides the same behavior as
|
||||||
|
the mv shell command. The previous behavior raised an exception. Patch by
|
||||||
|
Jeffrey Kintscher.
|
Loading…
Reference in New Issue