diff options
author | Jeffrey Kintscher <49998481+websurfer5@users.noreply.github.com> | 2023-12-27 16:23:42 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 16:23:42 (GMT) |
commit | c66b577d9f7a11ffab57985fd6fb22e9dfd4f245 (patch) | |
tree | 9f1f83c16e016d6d4b6d4025d596c109318f27a5 | |
parent | 1b19d7376818d14ab865fa22cb66baeacdb88277 (diff) | |
download | cpython-c66b577d9f7a11ffab57985fd6fb22e9dfd4f245.zip cpython-c66b577d9f7a11ffab57985fd6fb22e9dfd4f245.tar.gz cpython-c66b577d9f7a11ffab57985fd6fb22e9dfd4f245.tar.bz2 |
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)
-rw-r--r-- | Lib/shutil.py | 2 | ||||
-rw-r--r-- | Lib/test/test_shutil.py | 29 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-08-06-14-43-55.bpo-26791.KxoEfO.rst | 4 |
3 files changed, 34 insertions, 1 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py index c40f6dd..acc9419 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -885,7 +885,7 @@ def move(src, dst, copy_function=copy2): sys.audit("shutil.move", src, dst) real_dst = 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, # perform the rename anyway. os.rename(src, dst) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index cc5459a..8edd75e 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2688,6 +2688,35 @@ class TestMove(BaseTest, unittest.TestCase): finally: 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 @unittest.skipUnless(hasattr(os, 'lchflags') diff --git a/Misc/NEWS.d/next/Library/2020-08-06-14-43-55.bpo-26791.KxoEfO.rst b/Misc/NEWS.d/next/Library/2020-08-06-14-43-55.bpo-26791.KxoEfO.rst new file mode 100644 index 0000000..c6f8dcb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-06-14-43-55.bpo-26791.KxoEfO.rst @@ -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. |