[Python-checkins] cpython: Issue #9993: When the source and destination are on different filesystems,

antoine.pitrou python-checkins at python.org
Fri Jan 6 20:17:44 CET 2012


http://hg.python.org/cpython/rev/1ea8b7233fd7
changeset: 74288:1ea8b7233fd7
user: Antoine Pitrou <solipsis at pitrou.net>
date: Fri Jan 06 20:16:19 2012 +0100
summary:
 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.
files:
 Doc/library/shutil.rst | 7 ++++-
 Lib/shutil.py | 11 ++++++-
 Lib/test/test_shutil.py | 43 +++++++++++++++++++++++++++++
 Misc/ACKS | 1 +
 Misc/NEWS | 5 +++
 5 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -196,7 +196,12 @@
 
 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.
+ 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
+ it to the behavior of GNU's :program:`mv`.
 
 .. function:: disk_usage(path)
 
diff --git a/Lib/shutil.py b/Lib/shutil.py
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -356,7 +356,10 @@
 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.
+ 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 @@
 try:
 os.rename(src, real_dst)
 except OSError:
- if os.path.isdir(src):
+ if os.path.islink(src):
+ linkto = os.readlink(src)
+ os.symlink(linkto, real_dst)
+ os.unlink(src)
+ elif os.path.isdir(src):
 if _destinsrc(src, dst):
 raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst))
 copytree(src, real_dst, symlinks=True)
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -1104,6 +1104,49 @@
 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')
+ os.symlink(self.src_file, dst)
+ shutil.move(dst, self.dst_file)
+ self.assertTrue(os.path.islink(self.dst_file))
+ self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
+
+ @support.skip_unless_symlink
+ @mock_rename
+ def test_move_file_symlink_to_dir(self):
+ filename = "bar"
+ dst = os.path.join(self.src_dir, filename)
+ os.symlink(self.src_file, dst)
+ shutil.move(dst, self.dst_dir)
+ final_link = os.path.join(self.dst_dir, filename)
+ self.assertTrue(os.path.islink(final_link))
+ self.assertTrue(os.path.samefile(self.src_file, final_link))
+
+ @support.skip_unless_symlink
+ @mock_rename
+ def test_move_dangling_symlink(self):
+ src = os.path.join(self.src_dir, 'baz')
+ dst = os.path.join(self.src_dir, 'bar')
+ os.symlink(src, dst)
+ dst_link = os.path.join(self.dst_dir, 'quux')
+ shutil.move(dst, dst_link)
+ self.assertTrue(os.path.islink(dst_link))
+ self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
+
+ @support.skip_unless_symlink
+ @mock_rename
+ def test_move_dir_symlink(self):
+ src = os.path.join(self.src_dir, 'baz')
+ dst = os.path.join(self.src_dir, 'bar')
+ os.mkdir(src)
+ os.symlink(src, dst)
+ dst_link = os.path.join(self.dst_dir, 'quux')
+ shutil.move(dst, dst_link)
+ self.assertTrue(os.path.islink(dst_link))
+ self.assertTrue(os.path.samefile(src, dst_link))
+
 
 class TestCopyFile(unittest.TestCase):
 
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -707,6 +707,7 @@
 George Neville-Neil
 Johannes Nicolai
 Samuel Nicolary
+Jonathan Niehof
 Gustavo Niemeyer
 Oscar Nierstrasz
 Hrvoje Niksic
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -422,6 +422,11 @@
 Library
 -------
 
+- 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.
+
 - Issue #12926: Fix a bug in tarfile's link extraction.
 
 - Issue #13696: Fix the 302 Relative URL Redirection problem.
-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /