cpython: 1ea8b7233fd7 (original) (raw)
Mercurial > cpython
changeset 74288:1ea8b7233fd7
Issue #9993: When the source and destination are on different filesystems, and the source is a symlink, shutil.move() now recreates a symlink on the destination instead of copying the file contents. Patch by Jonathan Niehof and Hynek Schlawack. [#9993]
Antoine Pitrou solipsis@pitrou.net | |
---|---|
date | Fri, 06 Jan 2012 20:16:19 +0100 |
parents | a4c19a946a5d |
children | 9f0728521731 |
files | Doc/library/shutil.rst Lib/shutil.py Lib/test/test_shutil.py Misc/ACKS Misc/NEWS |
diffstat | 5 files changed, 64 insertions(+), 3 deletions(-)[+] [-] Doc/library/shutil.rst 7 Lib/shutil.py 11 Lib/test/test_shutil.py 43 Misc/ACKS 1 Misc/NEWS 5 |
line wrap: on
line diff
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -196,7 +196,12 @@ Directory and files operations
If the destination is on the current filesystem, then :func:os.rename
is
used. Otherwise, src is copied (using :func:copy2
) to dst and then
- removed. In case of symlinks, a new symlink pointing to the target of src
- will be created in or as dst and src will be removed. +
- .. versionchanged:: 3.3
Added explicit symlink handling for foreign filesystems, thus adapting[](#l1.12)
it to the behavior of GNU's :program:`mv`.[](#l1.13)
.. function:: disk_usage(path)
--- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -356,7 +356,10 @@ def move(src, dst): overwritten depending on os.rename() semantics. If the destination is on our current filesystem, then rename() is used.
- Otherwise, src is copied to the destination and then removed. Symlinks are
- recreated under the new name if os.rename() fails because of cross
- filesystem renames.
+ A lot more could be done here... A look at a mv.c shows a lot of the issues this implementation glosses over. @@ -375,7 +378,11 @@ def move(src, dst): try: os.rename(src, real_dst) except OSError:
if os.path.isdir(src):[](#l2.19)
if os.path.islink(src):[](#l2.20)
linkto = os.readlink(src)[](#l2.21)
os.symlink(linkto, real_dst)[](#l2.22)
os.unlink(src)[](#l2.23)
elif os.path.isdir(src):[](#l2.24) if _destinsrc(src, dst):[](#l2.25) raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst))[](#l2.26) copytree(src, real_dst, symlinks=True)[](#l2.27)
--- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1104,6 +1104,49 @@ class TestMove(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True)
- @support.skip_unless_symlink
- @mock_rename
- def test_move_file_symlink(self):
dst = os.path.join(self.src_dir, 'bar')[](#l3.10)
os.symlink(self.src_file, dst)[](#l3.11)
shutil.move(dst, self.dst_file)[](#l3.12)
self.assertTrue(os.path.islink(self.dst_file))[](#l3.13)
self.assertTrue(os.path.samefile(self.src_file, self.dst_file))[](#l3.14)
- @support.skip_unless_symlink
- @mock_rename
- def test_move_file_symlink_to_dir(self):
filename = "bar"[](#l3.19)
dst = os.path.join(self.src_dir, filename)[](#l3.20)
os.symlink(self.src_file, dst)[](#l3.21)
shutil.move(dst, self.dst_dir)[](#l3.22)
final_link = os.path.join(self.dst_dir, filename)[](#l3.23)
self.assertTrue(os.path.islink(final_link))[](#l3.24)
self.assertTrue(os.path.samefile(self.src_file, final_link))[](#l3.25)
- @support.skip_unless_symlink
- @mock_rename
- def test_move_dangling_symlink(self):
src = os.path.join(self.src_dir, 'baz')[](#l3.30)
dst = os.path.join(self.src_dir, 'bar')[](#l3.31)
os.symlink(src, dst)[](#l3.32)
dst_link = os.path.join(self.dst_dir, 'quux')[](#l3.33)
shutil.move(dst, dst_link)[](#l3.34)
self.assertTrue(os.path.islink(dst_link))[](#l3.35)
self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))[](#l3.36)
- @support.skip_unless_symlink
- @mock_rename
- def test_move_dir_symlink(self):
src = os.path.join(self.src_dir, 'baz')[](#l3.41)
dst = os.path.join(self.src_dir, 'bar')[](#l3.42)
os.mkdir(src)[](#l3.43)
os.symlink(src, dst)[](#l3.44)
dst_link = os.path.join(self.dst_dir, 'quux')[](#l3.45)
shutil.move(dst, dst_link)[](#l3.46)
self.assertTrue(os.path.islink(dst_link))[](#l3.47)
self.assertTrue(os.path.samefile(src, dst_link))[](#l3.48)
+ class TestCopyFile(unittest.TestCase):
--- a/Misc/ACKS +++ b/Misc/ACKS @@ -707,6 +707,7 @@ Max Neunhöffer George Neville-Neil Johannes Nicolai Samuel Nicolary +Jonathan Niehof Gustavo Niemeyer Oscar Nierstrasz Hrvoje Niksic
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -422,6 +422,11 @@ Core and Builtins Library ------- +- Issue #9993: When the source and destination are on different filesystems,