Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 9 additions & 1 deletion fs/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -82,7 +85,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.
Expand Down
24 changes: 24 additions & 0 deletions tests/test_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,30 @@ 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
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):
with open_fs("mem://") as src, open_fs("mem://") as dst:
Expand Down