diff -r 7ab1c55fcf82 Doc/library/os.rst --- a/Doc/library/os.rst Sun Mar 03 10:53:27 2013 -0800 +++ b/Doc/library/os.rst Sun Mar 03 22:47:12 2013 -0500 @@ -1720,18 +1720,34 @@ .. function:: rename(src, dst, *, src_dir_fd=None, dst_dir_fd=None) - Rename the file or directory *src* to *dst*. If *dst* is a directory, - :exc:`OSError` will be raised. On Unix, if *dst* exists and is a file, it will - be replaced silently if the user has permission. The operation may fail on some - Unix flavors if *src* and *dst* are on different filesystems. If successful, - the renaming will be an atomic operation (this is a POSIX requirement). On - Windows, if *dst* already exists, :exc:`OSError` will be raised even if it is a - file. + Rename the file or directory *src* to *dst*. If *src* exists as either + a file or directory and *dst* does not exist the rename will occur with + no error raised. In some cases the rename function will behave + differently across platforms which are noted below. In all cases + if *src* does not exist :exc:`OSError` will be raised. + + =================== ================= ===================== ================ + Source Destination + ------------------- -------------------------------------------------------- + Type File Empty Directory Non-Empty + Directory + =================== ================= ===================== ================ + File | unix success OSError OSError + | win OSError + Empty Directory OSError | unix success OSError + | win FileExistsError + Non-Empty Directory OSError | unix success OSError + | win FileExistsError + =================== ================= ===================== ================ + + If successful, the renaming will be an atomic operation (this is a POSIX + requirement). This function can support specifying *src_dir_fd* and/or *dst_dir_fd* to supply :ref:`paths relative to directory descriptors `. - If you want cross-platform overwriting of the destination, use :func:`replace`. + If you want cross-platform overwriting of the destination, use + :func:`replace`. Availability: Unix, Windows. diff -r 7ab1c55fcf82 Lib/test/test_os.py --- a/Lib/test/test_os.py Sun Mar 03 10:53:27 2013 -0800 +++ b/Lib/test/test_os.py Sun Mar 03 22:47:12 2013 -0500 @@ -52,12 +52,77 @@ # Issue #14110: Some tests fail on FreeBSD if the user is in the wheel group. HAVE_WHEEL_GROUP = sys.platform.startswith('freebsd') and os.getgid() == 0 -# Tests creating TESTFN +# Tests creating TESTFN and os.rename class FileTests(unittest.TestCase): def setUp(self): if os.path.exists(support.TESTFN): os.unlink(support.TESTFN) - tearDown = setUp + + # create rename_src_file and place data in the file + self.rename_src_file = "%s_%d_tmp" % ("test_rename_src_file", \ + os.getpid()) + if os.path.exists(self.rename_src_file): + os.unlink(self.rename_src_file) + fd = os.open(self.rename_src_file, os.O_CREAT | os.O_WRONLY) + os.write(fd, b"beans\n") + os.write(fd, b"bacon\n") + os.write(fd, b"eggs\n") + os.write(fd, b"spam\n") + os.close(fd) + + # create rename_dst_file and place data in the file + self.rename_dst_file = "%s_%d_tmp" % ("test_rename_dst_file", \ + os.getpid()) + if os.path.exists(self.rename_dst_file): + os.unlink(self.rename_dst_file) + fd = os.open(self.rename_dst_file, os.O_CREAT | os.O_WRONLY) + os.write(fd, b"dst file already exists and has data") + os.close(fd) + + # create rename_dst_directory + self.rename_dst_directory = "%s_%d_tmp" % \ + ("test_rename_dst_directory", os.getpid()) + if (os.path.exists(self.rename_dst_directory + "/file.txt")): + os.unlink(self.rename_dst_directory + "/file.txt") + if (os.path.exists(self.rename_dst_directory)): + os.rmdir(self.rename_dst_directory) + os.mkdir(self.rename_dst_directory) + fd = os.open(self.rename_dst_directory + "/file.txt", \ + os.O_CREAT | os.O_WRONLY) + os.write(fd, b"dst file already exists and has data") + os.close(fd) + + # create rename_src_directory + self.rename_src_directory = "%s_%d_tmp" % \ + ("test_src_directory", os.getpid()) + if (os.path.exists(self.rename_src_directory + "/file.txt")): + os.unlink(self.rename_src_directory + "/file.txt") + if (os.path.exists(self.rename_src_directory)): + os.rmdir(self.rename_src_directory) + os.mkdir(self.rename_src_directory) + fd = os.open(self.rename_src_directory + "/file.txt", \ + os.O_CREAT | os.O_WRONLY) + os.write(fd, b"beans\n") + os.write(fd, b"bacon\n") + os.write(fd, b"eggs\n") + os.write(fd, b"spam\n") + os.close(fd) + + def tearDown(self): + if os.path.exists(support.TESTFN): + os.unlink(support.TESTFN) + if os.path.exists(self.rename_src_file): + os.unlink(self.rename_src_file) + if os.path.exists(self.rename_dst_file): + os.unlink(self.rename_dst_file) + if os.path.exists(self.rename_dst_directory + "/file.txt"): + os.unlink(self.rename_dst_directory + "/file.txt") + if os.path.exists(self.rename_dst_directory): + os.rmdir(self.rename_dst_directory) + if os.path.exists(self.rename_src_directory + "/file.txt"): + os.unlink(self.rename_src_directory + "/file.txt") + if (os.path.exists(self.rename_src_directory)): + os.rmdir(self.rename_src_directory) def test_access(self): f = os.open(support.TESTFN, os.O_CREAT|os.O_RDWR) @@ -93,6 +158,247 @@ new = sys.getrefcount(path) self.assertEqual(old, new) + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_dest_file_exist(self): + if ((sys.platform == 'darwin') or (sys.platform.startswith == "linux")): + os.rename(self.rename_src_file, self.rename_dst_file) + with open(self.rename_dst_file, "rb") as fobj: + self.assertEqual(fobj.read().splitlines(), + [b"beans", b"bacon", b"eggs", b"spam"]) + elif (sys.platform == 'win'): + self.assertRaises(OSError, os.rename, src, dst) + with open(self.rename_dst_file, "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_dst_not_exist(self): + if os.path.exists(self.rename_dst_file): + os.unlink(self.rename_dst_file) # delete rename_dst_file + os.rename(self.rename_src_file, self.rename_dst_file) + with open(self.rename_dst_file, "rb") as fobj: + self.assertEqual(fobj.read().splitlines(), + [b"beans", b"bacon", b"eggs", b"spam"]) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_dst_directory_empty(self): + if (os.path.exists(self.rename_dst_file + "/file.txt")): + os.unlink(self.rename_dst_file + "/file.txt") # delete file in dir + self.assertRaises(OSError, os.rename, self.rename_src_file, \ + self.rename_dst_directory) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_dst_directory_not_empty(self): + self.assertRaises(OSError, os.rename, self.rename_src_file, \ + self.rename_dst_directory) + with open(self.rename_dst_directory + "/file.txt", "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_empty_dst_file_exist(self): + if (os.path.exists(self.rename_src_directory + "/file.txt")): + os.unlink(self.rename_src_directory + "/file.txt") + self.assertRaises(OSError, os.rename, self.rename_src_directory, \ + self.rename_dst_file) + with open(self.rename_dst_file, "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_empty_dst_not_exist(self): + if (os.path.exists(self.rename_src_directory + "/file.txt")): + os.unlink(self.rename_src_directory + "/file.txt") + if (os.path.exists(self.rename_dst_directory + "/file.txt")): + os.unlink(self.rename_dst_directory + "/file.txt") + if (os.path.exists(self.rename_dst_directory)): + os.rmdir(self.rename_dst_directory) + + os.rename(self.rename_src_directory, self.rename_dst_directory) + self.assertEqual(os.path.exists(self.rename_dst_directory), True) + self.assertEqual(os.path.exists(self.rename_src_directory), False) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_empty_dst_directory_empty(self): + if (os.path.exists(self.rename_src_directory + "/file.txt")): + os.unlink(self.rename_src_directory + "/file.txt") + if (os.path.exists(self.rename_dst_directory + "/file.txt")): + os.unlink(self.rename_dst_directory + "/file.txt") + if ((sys.platform.startswith == 'linux') or (sys.platform == 'darwin')): + os.rename(self.rename_src_directory, self.rename_dst_directory) + self.assertEqual(os.path.exists(self.rename_dst_directory), True) + self.assertEqual(os.path.exists(self.rename_src_directory), False) + elif (sys.platform == 'win32'): + self.assertRaises(FileExistsError, os.rename, \ + self.rename_src_directory, self.rename_dst_directory) + # both src and dst will still be intact on windows + self.assertEqual(os.path.exists(self.rename_dst_directory), True) + self.assertEqual(os.path.exists(self.rename_src_directory), True) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_empty_dst_directory_not_empty(self): + if (os.path.exists(self.rename_src_directory + "/file.txt")): + os.unlink(self.rename_src_directory + "/file.txt") + self.assertRaises(OSError, os.rename, self.rename_src_directory, \ + self.rename_dst_directory) + # destination file should be in tact + with open(self.rename_dst_directory + "/file.txt", "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_not_empty_dst_file_exist(self): + self.assertRaises(OSError, os.rename, self.rename_src_directory, \ + self.rename_dst_file) + with open(self.rename_dst_file, "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_not_empty_dst_not_exist(self): + # remove the dst directory and file + if (os.path.exists(self.rename_dst_directory + "/file.txt")): + os.unlink(self.rename_dst_directory + "/file.txt") + if (os.path.exists(self.rename_dst_directory)): + os.rmdir(self.rename_dst_directory) + os.rename(self.rename_src_directory, self.rename_dst_directory) + with open(self.rename_dst_directory + "/file.txt", "rb") as fobj: + self.assertEqual(fobj.read().splitlines(), + [b"beans", b"bacon", b"eggs", b"spam"]) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_not_empty_dst_directory_empty(self): + if (os.path.exists(self.rename_dst_directory + "/file.txt")): + os.unlink(self.rename_dst_directory + "/file.txt") + if ((sys.platform.startswith == 'linux') or (sys.platform == 'darwin')): + os.rename(self.rename_src_directory, self.rename_dst_directory) + with open(self.rename_dst_directory + "/file.txt", "rb") as fobj: + self.assertEqual(fobj.read().splitlines(), + [b"beans", b"bacon", b"eggs", b"spam"]) + elif (sys.platform == 'win32'): + self.assertRaises(FileExistsError, os.rename, \ + self.rename_src_directory, self.rename_dst_directory) + # both src and dst will still be intact on windows + self.assertEqual(os.path.exists(self.rename_dst_directory), True) + self.assertEqual(os.path.exists(self.rename_src_directory), True) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_directory_not_empty_dst_directory_not_empty(self): + self.assertRaises(OSError, os.rename, self.rename_src_directory, \ + self.rename_dst_directory) + # destination file should be in tact + with open(self.rename_dst_directory + "/file.txt", "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_or_directory_not_exist_dst_file_exist(self): + if (os.path.exists(self.rename_src_file)): + os.unlink(self.rename_src_file) + self.assertRaises(OSError, os.rename, self.rename_src_file, \ + self.rename_dst_file) + # destination file should be in tact + with open(self.rename_dst_file, "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_or_directory_not_exist_dst_not_exist(self): + if (os.path.exists(self.rename_src_file)): + os.unlink(self.rename_src_file) + if (os.path.exists(self.rename_dst_file)): + os.unlink(self.rename_dst_file) + src = self.rename_src_file + "does_not_exist" + dst = self.rename_dst_file + "does_not_exist" + self.assertRaises(OSError, os.rename, self.rename_src_file, \ + self.rename_dst_file) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_or_directory_not_exist_dst_directory_empty(self): + if (os.path.exists(self.rename_src_file)): + os.unlink(self.rename_src_file) + if (os.path.exists(self.rename_dst_directory + "/file.txt")): + os.unlink(self.rename_dst_directory + "/file.txt") + self.assertRaises(OSError, os.rename, self.rename_src_file, \ + self.rename_dst_directory) + self.assertEqual(os.path.exists(self.rename_dst_directory), True) + + @support.cpython_only + @unittest.skipUnless(( (sys.platform == 'darwin') or \ + (sys.platform.startswith == 'linux') or \ + (sys.platform == 'win32') ), \ + 'test specific to windows or linux or darwin only') + def test_rename_src_file_or_directory_not_exist_dst_directory_not_empty(self): + if (os.path.exists(self.rename_src_file)): + os.unlink(self.rename_src_file) + self.assertRaises(OSError, os.rename, self.rename_src_file, \ + self.rename_dst_directory) + self.assertEqual(os.path.exists(self.rename_dst_directory), True) + # destination file should be in tact + with open(self.rename_dst_directory + "/file.txt", "rb") as fobj: + self.assertEqual(fobj.read(), + b"dst file already exists and has data") + def test_read(self): with open(support.TESTFN, "w+b") as fobj: fobj.write(b"spam")