From 6763644ad976ad9e2ccb229b5d45ec88f9d4ad44 Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Thu, 7 Jul 2022 15:42:40 +0200 Subject: [PATCH 01/14] add default overwrite arg (fixes #535) --- fs/move.py | 7 ++++++- tests/test_move.py | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/move.py b/fs/move.py index fdbe96fe..752b5816 100644 --- a/fs/move.py +++ b/fs/move.py @@ -82,7 +82,12 @@ def move_file( rel_dst = frombase(common, dst_syspath) with _src_fs.lock(), _dst_fs.lock(): with OSFS(common) as base: - base.move(rel_src, rel_dst, preserve_time=preserve_time) + base.move( + rel_src, + rel_dst, + overwrite=True, + preserve_time=preserve_time, + ) return # optimization worked, exit early except ValueError: # This is raised if we cannot find a common base folder. diff --git a/tests/test_move.py b/tests/test_move.py index 5401082e..3692d06d 100644 --- a/tests/test_move.py +++ b/tests/test_move.py @@ -151,6 +151,14 @@ def test_move_file_read_only_mem_dest(self): dst_ro.exists("target.txt"), "file should not have been copied over" ) + def test_overwrite(self): + with open_fs("temp://") as src, open_fs("temp://") as dst: + src.writetext("file.txt", "Content") + dst.writetext("target.txt", "Content") + fs.move.move_file(src, "file.txt", dst, "target.txt") + self.assertFalse(src.exists("file.txt")) + self.assertTrue(dst.exists("target.txt")) + @parameterized.expand([(True,), (False,)]) def test_move_file_cleanup_on_error(self, cleanup): with open_fs("mem://") as src, open_fs("mem://") as dst: From 4eb0854f97f51272a5eb3d56cd03b2e94cd5bf19 Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Thu, 7 Jul 2022 15:47:32 +0200 Subject: [PATCH 02/14] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 267c942b..10c794b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- Fixes a backward incompatibility where `fs.move.move_file` raises `DestinationExists` + ([#535](https://github.com/PyFilesystem/pyfilesystem2/issues/535)). + ## [2.4.16] - 2022-05-02 From 17575bac0cdd7ea2cd6738828306ede0ec57f7ab Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Fri, 8 Jul 2022 11:34:20 +0200 Subject: [PATCH 03/14] add tests --- tests/test_move.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/test_move.py b/tests/test_move.py index 3692d06d..3cb841ed 100644 --- a/tests/test_move.py +++ b/tests/test_move.py @@ -151,13 +151,29 @@ def test_move_file_read_only_mem_dest(self): dst_ro.exists("target.txt"), "file should not have been copied over" ) - def test_overwrite(self): - with open_fs("temp://") as src, open_fs("temp://") as dst: - src.writetext("file.txt", "Content") - dst.writetext("target.txt", "Content") + @parameterized.expand([("temp://",), ("mem://",)]) + def test_move_file_overwrite(self, fs_url): + # we use TempFS and MemoryFS in order to make sure the optimised code path + # behaves like the regular one + with open_fs(fs_url) as src, open_fs(fs_url) as dst: + src.writetext("file.txt", "source content") + dst.writetext("target.txt", "target content") fs.move.move_file(src, "file.txt", dst, "target.txt") self.assertFalse(src.exists("file.txt")) + self.assertFalse(src.exists("target.txt")) + self.assertFalse(dst.exists("file.txt")) self.assertTrue(dst.exists("target.txt")) + self.assertEquals(dst.readtext("target.txt"), "source content") + + @parameterized.expand([("temp://",), ("mem://",)]) + def test_move_file_overwrite_itself(self, fs_url): + # we use TempFS and MemoryFS in order to make sure the optimised code path + # behaves like the regular one + with open_fs(fs_url) as tmp: + tmp.writetext("file.txt", "content") + fs.move.move_file(tmp, "file.txt", tmp, "file.txt") + self.assertTrue(tmp.exists("file.txt")) + self.assertEquals(tmp.readtext("file.txt"), "content") @parameterized.expand([(True,), (False,)]) def test_move_file_cleanup_on_error(self, cleanup): From bdfc7743ce9dd4b0fdeecccc06db22189d310066 Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Fri, 8 Jul 2022 11:34:39 +0200 Subject: [PATCH 04/14] fix deletion when moving file on itself --- fs/move.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/move.py b/fs/move.py index 752b5816..49f93483 100644 --- a/fs/move.py +++ b/fs/move.py @@ -64,6 +64,9 @@ def move_file( with manage_fs(src_fs, writeable=True) as _src_fs: with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: if _src_fs is _dst_fs: + if src_path == dst_path: + return + # Same filesystem, may be optimized _src_fs.move( src_path, dst_path, overwrite=True, preserve_time=preserve_time From 863dc8322c71c9c61db5a9a8285605108546411e Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Thu, 21 Jul 2022 13:19:35 +0200 Subject: [PATCH 05/14] compare normalized pathes in early exit --- fs/move.py | 5 +++-- tests/test_move.py | 31 +++++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/fs/move.py b/fs/move.py index 49f93483..d2ac8add 100644 --- a/fs/move.py +++ b/fs/move.py @@ -10,7 +10,7 @@ from .errors import FSError from .opener import manage_fs from .osfs import OSFS -from .path import frombase +from .path import frombase, normpath if typing.TYPE_CHECKING: from typing import Text, Union @@ -64,7 +64,8 @@ def move_file( with manage_fs(src_fs, writeable=True) as _src_fs: with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: if _src_fs is _dst_fs: - if src_path == dst_path: + # Exit early if source and destination are the same file + if normpath(src_path) == normpath(dst_path): return # Same filesystem, may be optimized diff --git a/tests/test_move.py b/tests/test_move.py index 3cb841ed..8eb1af75 100644 --- a/tests/test_move.py +++ b/tests/test_move.py @@ -151,13 +151,17 @@ def test_move_file_read_only_mem_dest(self): dst_ro.exists("target.txt"), "file should not have been copied over" ) - @parameterized.expand([("temp://",), ("mem://",)]) - def test_move_file_overwrite(self, fs_url): - # we use TempFS and MemoryFS in order to make sure the optimised code path - # behaves like the regular one + @parameterized.expand([("temp", "temp://"), ("mem", "mem://")]) + def test_move_file_overwrite(self, _, fs_url): + # we use TempFS and MemoryFS in order to make sure the optimized code path + # behaves like the regular one (TempFS tests the optmized code path). with open_fs(fs_url) as src, open_fs(fs_url) as dst: src.writetext("file.txt", "source content") dst.writetext("target.txt", "target content") + self.assertTrue(src.exists("file.txt")) + self.assertFalse(src.exists("target.txt")) + self.assertFalse(dst.exists("file.txt")) + self.assertTrue(dst.exists("target.txt")) fs.move.move_file(src, "file.txt", dst, "target.txt") self.assertFalse(src.exists("file.txt")) self.assertFalse(src.exists("target.txt")) @@ -165,16 +169,27 @@ def test_move_file_overwrite(self, fs_url): self.assertTrue(dst.exists("target.txt")) self.assertEquals(dst.readtext("target.txt"), "source content") - @parameterized.expand([("temp://",), ("mem://",)]) - def test_move_file_overwrite_itself(self, fs_url): - # we use TempFS and MemoryFS in order to make sure the optimised code path - # behaves like the regular one + @parameterized.expand([("temp", "temp://"), ("mem", "mem://")]) + def test_move_file_overwrite_itself(self, _, fs_url): + # we use TempFS and MemoryFS in order to make sure the optimized code path + # behaves like the regular one (TempFS tests the optmized code path). with open_fs(fs_url) as tmp: tmp.writetext("file.txt", "content") fs.move.move_file(tmp, "file.txt", tmp, "file.txt") self.assertTrue(tmp.exists("file.txt")) self.assertEquals(tmp.readtext("file.txt"), "content") + @parameterized.expand([("temp", "temp://"), ("mem", "mem://")]) + def test_move_file_overwrite_itself_relpath(self, _, fs_url): + # we use TempFS and MemoryFS in order to make sure the optimized code path + # behaves like the regular one (TempFS tests the optmized code path). + with open_fs(fs_url) as tmp: + new_dir = tmp.makedir("dir") + new_dir.writetext("file.txt", "content") + fs.move.move_file(tmp, "dir/../dir/file.txt", tmp, "dir/file.txt") + self.assertTrue(tmp.exists("dir/file.txt")) + self.assertEquals(tmp.readtext("dir/file.txt"), "content") + @parameterized.expand([(True,), (False,)]) def test_move_file_cleanup_on_error(self, cleanup): with open_fs("mem://") as src, open_fs("mem://") as dst: From 38e6b3cf524d714541b52f8d69e9f87d9699490e Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 2 Aug 2022 13:07:22 +0200 Subject: [PATCH 06/14] check no longer needed --- fs/move.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/move.py b/fs/move.py index d2ac8add..f06e7057 100644 --- a/fs/move.py +++ b/fs/move.py @@ -64,10 +64,6 @@ def move_file( with manage_fs(src_fs, writeable=True) as _src_fs: with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: if _src_fs is _dst_fs: - # Exit early if source and destination are the same file - if normpath(src_path) == normpath(dst_path): - return - # Same filesystem, may be optimized _src_fs.move( src_path, dst_path, overwrite=True, preserve_time=preserve_time From 93c2579f14fab4261c45178ccf5689e05f326426 Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 2 Aug 2022 13:10:26 +0200 Subject: [PATCH 07/14] remove unused import --- fs/move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/move.py b/fs/move.py index f06e7057..752b5816 100644 --- a/fs/move.py +++ b/fs/move.py @@ -10,7 +10,7 @@ from .errors import FSError from .opener import manage_fs from .osfs import OSFS -from .path import frombase, normpath +from .path import frombase if typing.TYPE_CHECKING: from typing import Text, Union From fb0b35a5c69cc762f8ea18f253d467d03b03b04a Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 30 Aug 2022 14:45:23 +0200 Subject: [PATCH 08/14] add optimization --- CHANGELOG.md | 2 ++ fs/move.py | 39 +++++++++++++++++++++++++++++++-------- tests/test_move.py | 20 +++++++++++++++++--- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef16734e..3823095c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- Optimized moving folders between filesystems with syspaths + ([#550](https://github.com/PyFilesystem/pyfilesystem2/pull/550)) ### Added diff --git a/fs/move.py b/fs/move.py index 752b5816..f68a4e1a 100644 --- a/fs/move.py +++ b/fs/move.py @@ -5,12 +5,15 @@ import typing +import os + from ._pathcompat import commonpath from .copy import copy_dir, copy_file -from .errors import FSError +from .error_tools import convert_os_errors +from .errors import DirectoryExpected, FSError, IllegalDestination, ResourceNotFound from .opener import manage_fs from .osfs import OSFS -from .path import frombase +from .path import frombase, isbase if typing.TYPE_CHECKING: from typing import Text, Union @@ -26,7 +29,6 @@ def move_fs( ): # type: (...) -> None """Move the contents of a filesystem to another filesystem. - Arguments: src_fs (FS or str): Source filesystem (instance or URL). dst_fs (FS or str): Destination filesystem (instance or URL). @@ -34,7 +36,6 @@ def move_fs( a single-threaded copy. preserve_time (bool): If `True`, try to preserve mtime of the resources (defaults to `False`). - """ move_dir(src_fs, "/", dst_fs, "/", workers=workers, preserve_time=preserve_time) @@ -49,7 +50,6 @@ def move_file( ): # type: (...) -> None """Move a file from one filesystem to another. - Arguments: src_fs (FS or str): Source filesystem (instance or URL). src_path (str): Path to a file on ``src_fs``. @@ -59,7 +59,6 @@ def move_file( resources (defaults to `False`). cleanup_dst_on_error (bool): If `True`, tries to delete the file copied to ``dst_fs`` if deleting the file from ``src_fs`` fails (defaults to `True`). - """ with manage_fs(src_fs, writeable=True) as _src_fs: with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: @@ -123,7 +122,6 @@ def move_dir( ): # type: (...) -> None """Move a directory from one filesystem to another. - Arguments: src_fs (FS or str): Source filesystem (instance or URL). src_path (str): Path to a directory on ``src_fs`` @@ -133,10 +131,35 @@ def move_dir( (default) for a single-threaded copy. preserve_time (bool): If `True`, try to preserve mtime of the resources (defaults to `False`). - + Raises: + fs.errors.ResourceNotFound: if ``src_path`` does not exist on `src_fs` + fs.errors.DirectoryExpected: if ``src_path`` or one of its + ancestors is not a directory. + fs.errors.IllegalDestination: when moving a folder into itself """ with manage_fs(src_fs, writeable=True) as _src_fs: with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: + if not _src_fs.exists(src_path): + raise ResourceNotFound(src_path) + if not _src_fs.isdir(src_path): + raise DirectoryExpected(src_path) + + # if both filesystems have a syspath we use `os.rename` to move the folder + if _src_fs.hassyspath(src_path) and _dst_fs.hassyspath(dst_path): + src_syspath = _src_fs.getsyspath(src_path) + dst_syspath = _dst_fs.getsyspath(dst_path) + # recheck if the move operation is legal using the syspaths + if isbase(src_syspath, dst_syspath): + raise IllegalDestination(dst_path) + with _src_fs.lock(), _dst_fs.lock(): + with convert_os_errors("move_dir", src_path, directory=True): + os.rename(src_syspath, dst_syspath) + # recreate the root dir if it has been renamed + if src_path == "/": + _src_fs.makedir("/") + return # optimization worked, exit early + + # standard copy then delete with _src_fs.lock(), _dst_fs.lock(): _dst_fs.makedir(dst_path, recreate=True) copy_dir( diff --git a/tests/test_move.py b/tests/test_move.py index 8eb1af75..0aeb9631 100644 --- a/tests/test_move.py +++ b/tests/test_move.py @@ -7,6 +7,7 @@ except ImportError: import mock +import os from parameterized import parameterized, parameterized_class import fs.move @@ -167,7 +168,7 @@ def test_move_file_overwrite(self, _, fs_url): self.assertFalse(src.exists("target.txt")) self.assertFalse(dst.exists("file.txt")) self.assertTrue(dst.exists("target.txt")) - self.assertEquals(dst.readtext("target.txt"), "source content") + self.assertEqual(dst.readtext("target.txt"), "source content") @parameterized.expand([("temp", "temp://"), ("mem", "mem://")]) def test_move_file_overwrite_itself(self, _, fs_url): @@ -177,7 +178,7 @@ def test_move_file_overwrite_itself(self, _, fs_url): tmp.writetext("file.txt", "content") fs.move.move_file(tmp, "file.txt", tmp, "file.txt") self.assertTrue(tmp.exists("file.txt")) - self.assertEquals(tmp.readtext("file.txt"), "content") + self.assertEqual(tmp.readtext("file.txt"), "content") @parameterized.expand([("temp", "temp://"), ("mem", "mem://")]) def test_move_file_overwrite_itself_relpath(self, _, fs_url): @@ -188,7 +189,7 @@ def test_move_file_overwrite_itself_relpath(self, _, fs_url): new_dir.writetext("file.txt", "content") fs.move.move_file(tmp, "dir/../dir/file.txt", tmp, "dir/file.txt") self.assertTrue(tmp.exists("dir/file.txt")) - self.assertEquals(tmp.readtext("dir/file.txt"), "content") + self.assertEqual(tmp.readtext("dir/file.txt"), "content") @parameterized.expand([(True,), (False,)]) def test_move_file_cleanup_on_error(self, cleanup): @@ -206,3 +207,16 @@ def test_move_file_cleanup_on_error(self, cleanup): ) self.assertTrue(src.exists("file.txt")) self.assertEqual(not dst.exists("target.txt"), cleanup) + + @parameterized.expand([("temp", "temp://", True), ("mem", "mem://", False)]) + def test_move_dir_optimized(self, _, fs_url, mv_called): + with open_fs(fs_url) as tmp: + with mock.patch.object(os, "rename", wraps=os.rename) as mv: + dir_ = tmp.makedir("dir") + dir_.writetext("file.txt", "content") + sub = dir_.makedir("sub") + sub.writetext("file.txt", "sub content") + fs.move.move_dir(tmp, "dir", tmp, "newdir") + self.assertEqual(tmp.readtext("newdir/file.txt"), "content") + self.assertEqual(tmp.readtext("newdir/sub/file.txt"), "sub content") + self.assertEqual(mv.called, mv_called) From cb2282de36f50aeb3d030fb7a0d15c878b2ee959 Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 30 Aug 2022 14:49:56 +0200 Subject: [PATCH 09/14] docstyle --- fs/move.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/move.py b/fs/move.py index f68a4e1a..9cb5df2a 100644 --- a/fs/move.py +++ b/fs/move.py @@ -29,6 +29,7 @@ def move_fs( ): # type: (...) -> None """Move the contents of a filesystem to another filesystem. + Arguments: src_fs (FS or str): Source filesystem (instance or URL). dst_fs (FS or str): Destination filesystem (instance or URL). @@ -36,6 +37,7 @@ def move_fs( a single-threaded copy. preserve_time (bool): If `True`, try to preserve mtime of the resources (defaults to `False`). + """ move_dir(src_fs, "/", dst_fs, "/", workers=workers, preserve_time=preserve_time) @@ -50,6 +52,7 @@ def move_file( ): # type: (...) -> None """Move a file from one filesystem to another. + Arguments: src_fs (FS or str): Source filesystem (instance or URL). src_path (str): Path to a file on ``src_fs``. @@ -59,6 +62,7 @@ def move_file( resources (defaults to `False`). cleanup_dst_on_error (bool): If `True`, tries to delete the file copied to ``dst_fs`` if deleting the file from ``src_fs`` fails (defaults to `True`). + """ with manage_fs(src_fs, writeable=True) as _src_fs: with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: @@ -122,6 +126,7 @@ def move_dir( ): # type: (...) -> None """Move a directory from one filesystem to another. + Arguments: src_fs (FS or str): Source filesystem (instance or URL). src_path (str): Path to a directory on ``src_fs`` @@ -131,11 +136,13 @@ def move_dir( (default) for a single-threaded copy. preserve_time (bool): If `True`, try to preserve mtime of the resources (defaults to `False`). + Raises: fs.errors.ResourceNotFound: if ``src_path`` does not exist on `src_fs` fs.errors.DirectoryExpected: if ``src_path`` or one of its ancestors is not a directory. fs.errors.IllegalDestination: when moving a folder into itself + """ with manage_fs(src_fs, writeable=True) as _src_fs: with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: From e6011ca5bcc252a20b27cfdc825ba5bd2e14ff42 Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 30 Aug 2022 15:01:35 +0200 Subject: [PATCH 10/14] fix win bugs? --- fs/move.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/move.py b/fs/move.py index 9cb5df2a..447efe17 100644 --- a/fs/move.py +++ b/fs/move.py @@ -159,10 +159,10 @@ def move_dir( if isbase(src_syspath, dst_syspath): raise IllegalDestination(dst_path) with _src_fs.lock(), _dst_fs.lock(): - with convert_os_errors("move_dir", src_path, directory=True): + with convert_os_errors("move_dir", dst_path, directory=True): os.rename(src_syspath, dst_syspath) # recreate the root dir if it has been renamed - if src_path == "/": + if src_path == "/" and not _src_fs.exists("/"): _src_fs.makedir("/") return # optimization worked, exit early From 3ad30a29e2bd262178277ddef925535a3e50400a Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 30 Aug 2022 15:03:29 +0200 Subject: [PATCH 11/14] simpler recreation code --- fs/move.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/move.py b/fs/move.py index 447efe17..61cb5747 100644 --- a/fs/move.py +++ b/fs/move.py @@ -162,8 +162,7 @@ def move_dir( with convert_os_errors("move_dir", dst_path, directory=True): os.rename(src_syspath, dst_syspath) # recreate the root dir if it has been renamed - if src_path == "/" and not _src_fs.exists("/"): - _src_fs.makedir("/") + _src_fs.makedir("/", recreate=True) return # optimization worked, exit early # standard copy then delete From f0ac06eaf4e84d4962afe8f060a74fc565373f14 Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 30 Aug 2022 15:16:13 +0200 Subject: [PATCH 12/14] fix win? --- fs/move.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/move.py b/fs/move.py index 61cb5747..e1543770 100644 --- a/fs/move.py +++ b/fs/move.py @@ -160,6 +160,8 @@ def move_dir( raise IllegalDestination(dst_path) with _src_fs.lock(), _dst_fs.lock(): with convert_os_errors("move_dir", dst_path, directory=True): + if _dst_fs.exists(dst_path): + os.rmdir(dst_syspath) os.rename(src_syspath, dst_syspath) # recreate the root dir if it has been renamed _src_fs.makedir("/", recreate=True) From 940fd8cb3c6df9ec8e4644d27f2193bb4567010b Mon Sep 17 00:00:00 2001 From: Thomas Feldmann Date: Tue, 30 Aug 2022 15:22:34 +0200 Subject: [PATCH 13/14] use `shutil.move` --- fs/move.py | 3 ++- tests/test_move.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/move.py b/fs/move.py index e1543770..14005266 100644 --- a/fs/move.py +++ b/fs/move.py @@ -6,6 +6,7 @@ import typing import os +import shutil from ._pathcompat import commonpath from .copy import copy_dir, copy_file @@ -162,7 +163,7 @@ def move_dir( with convert_os_errors("move_dir", dst_path, directory=True): if _dst_fs.exists(dst_path): os.rmdir(dst_syspath) - os.rename(src_syspath, dst_syspath) + shutil.move(src_syspath, dst_syspath) # recreate the root dir if it has been renamed _src_fs.makedir("/", recreate=True) return # optimization worked, exit early diff --git a/tests/test_move.py b/tests/test_move.py index 0aeb9631..736ade1b 100644 --- a/tests/test_move.py +++ b/tests/test_move.py @@ -7,7 +7,7 @@ except ImportError: import mock -import os +import shutil from parameterized import parameterized, parameterized_class import fs.move @@ -211,7 +211,7 @@ def test_move_file_cleanup_on_error(self, cleanup): @parameterized.expand([("temp", "temp://", True), ("mem", "mem://", False)]) def test_move_dir_optimized(self, _, fs_url, mv_called): with open_fs(fs_url) as tmp: - with mock.patch.object(os, "rename", wraps=os.rename) as mv: + with mock.patch.object(shutil, "move", wraps=shutil.move) as mv: dir_ = tmp.makedir("dir") dir_.writetext("file.txt", "content") sub = dir_.makedir("sub") From 7c34e41dbb713066ab7931adfbc2dbde6376fe14 Mon Sep 17 00:00:00 2001 From: "M.Eng. Thomas Feldmann" Date: Tue, 30 Aug 2022 16:04:06 +0200 Subject: [PATCH 14/14] Update move.py --- fs/move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/move.py b/fs/move.py index 14005266..0d776c02 100644 --- a/fs/move.py +++ b/fs/move.py @@ -152,7 +152,7 @@ def move_dir( if not _src_fs.isdir(src_path): raise DirectoryExpected(src_path) - # if both filesystems have a syspath we use `os.rename` to move the folder + # if both filesystems have a syspath we use `shutil.move` to move the folder if _src_fs.hassyspath(src_path) and _dst_fs.hassyspath(dst_path): src_syspath = _src_fs.getsyspath(src_path) dst_syspath = _dst_fs.getsyspath(dst_path)