diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -91,7 +91,8 @@ .. function:: copy(src, dst[, symlinks=False])) - Copy the file *src* to the file or directory *dst*. If *dst* is a directory, a + Copy the file *src* to the file or directory *dst* and return the file's + destination. If *dst* is a directory, a file with the same basename as *src* is created (or overwritten) in the directory specified. Permission bits are copied. *src* and *dst* are path names given as strings. If *symlinks* is true, symbolic links won't be @@ -103,7 +104,8 @@ .. function:: copy2(src, dst[, symlinks=False]) Similar to :func:`shutil.copy`, but metadata is copied as well -- in fact, - this is just :func:`shutil.copy` followed by :func:`copystat`. This is + this is just :func:`shutil.copy` followed by :func:`copystat`. As with + :func:`shutil.copy`, return the file's destination. This is similar to the Unix command :program:`cp -p`. If *symlinks* is true, symbolic links won't be followed but recreated instead -- this resembles GNU's :program:`cp -P`. @@ -189,7 +191,8 @@ .. function:: move(src, dst) - Recursively move a file or directory (*src*) to another location (*dst*). + Recursively move a file or directory (*src*) to another location (*dst*) + and return the destination. If the destination is a directory or a symlink to a directory, then *src* is moved inside that directory. diff --git a/Lib/shutil.py b/Lib/shutil.py --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -165,7 +165,7 @@ raise def copy(src, dst, symlinks=False): - """Copy data and mode bits ("cp src dst"). + """Copy data and mode bits ("cp src dst"). Return the file's destination. The destination may be a directory. @@ -177,9 +177,11 @@ dst = os.path.join(dst, os.path.basename(src)) copyfile(src, dst, symlinks=symlinks) copymode(src, dst, symlinks=symlinks) + return dst def copy2(src, dst, symlinks=False): - """Copy data and all stat info ("cp -p src dst"). + """Copy data and all stat info ("cp -p src dst"). Return the file's + destination." The destination may be a directory. @@ -191,6 +193,7 @@ dst = os.path.join(dst, os.path.basename(src)) copyfile(src, dst, symlinks=symlinks) copystat(src, dst, symlinks=symlinks) + return dst def ignore_patterns(*patterns): """Function that can be used as copytree() ignore parameter. @@ -346,7 +349,8 @@ def move(src, dst): """Recursively move a file or directory to another location. This is - similar to the Unix "mv" command. + similar to the Unix "mv" command. Return the file or directory's + destination. If the destination is a directory or a symlink to a directory, the source is moved inside the directory. The destination path must not already @@ -390,6 +394,7 @@ else: copy2(src, real_dst) os.unlink(src) + return real_dst def _destinsrc(src, dst): src = abspath(src) 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 @@ -992,6 +992,16 @@ shutil.chown(dirname, user, group) check_chown(dirname, uid, gid) + def test_copy_return_value(self): + # copy and copy2 both return their destination path. + for fn in (shutil.copy, shutil.copy2): + src_dir = self.mkdtemp() + dst_dir = self.mkdtemp() + src = os.path.join(src_dir, 'foo') + write_file(src, 'foo') + rv = fn(src, dst_dir) + self.assertEqual(rv, os.path.join(dst_dir, 'foo')) + class TestMove(unittest.TestCase): @@ -1148,6 +1158,11 @@ self.assertTrue(os.path.islink(dst_link)) self.assertTrue(os.path.samefile(src, dst_link)) + def test_move_return_value(self): + rv = shutil.move(self.src_file, self.dst_dir) + self.assertEqual(rv, + os.path.join(self.dst_dir, os.path.basename(self.src_file))) + class TestCopyFile(unittest.TestCase):