-
-
Notifications
You must be signed in to change notification settings - Fork 179
Optimize move.move_file
for moving files between different OSFS instances.
#523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
althonos
merged 42 commits into
PyFilesystem:master
from
tfeldmann:move_file-optimization
May 2, 2022
Merged
Changes from all commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
db837c5
add optimization to move
tfeldmann d6db460
update contributors
tfeldmann 43dc890
add test
tfeldmann cb49b6e
optimization not dependent of OSFS anymore
tfeldmann 1f7b0ff
Merge branch 'master' into move_file-optimization
tfeldmann 448f15f
remove unneeded import
tfeldmann 68846ce
lock src and dst for move operation
tfeldmann 003e1b7
handle ValueError
tfeldmann fa1803b
fix unbound var
tfeldmann 86adaa8
set "read_only": True in `WrapReadOnly.getmeta()`
tfeldmann 014123b
handle ro FSs in move_file optimization
tfeldmann 14d4fa2
add test for two different MemFS instances
tfeldmann a97c0c3
support FS URLs
tfeldmann bbe4389
clear use of fs url instead of str
tfeldmann 69022aa
add test for read-only sources
tfeldmann 545e016
more tests for read only sources
tfeldmann fd577df
clarify fallthrough and add writeable=True
tfeldmann 4edb1e0
cleanup destination if move fails
tfeldmann 20203d9
update changelog
tfeldmann ad7a970
raise exc in mange_fs if fs is not writeable
tfeldmann 1267194
don't run tests twice in parameterized_class
tfeldmann b0963bf
remove unneeded import
tfeldmann 4a102ce
update changelog
tfeldmann 763efc2
rename test case
tfeldmann e490452
formatting
tfeldmann 0c02f53
add python2.7 commonpath backport
tfeldmann 9be0381
fix path in ResourceReadOnly exception
tfeldmann baf7019
test_move cleanup
tfeldmann 69d1906
check time on if self.preserve_time is set
tfeldmann 7993af0
update changelog wording
tfeldmann 9cfc5dd
revert import order
tfeldmann 54d3374
ignore flake8 C401 in _pathcompat
tfeldmann c051f23
fix codestyle and typecheck errors
tfeldmann a549250
removed duplicated test code
tfeldmann 44cbe31
add `cleanup_dest_on_error` in `move_file`
tfeldmann 696c4ca
use non-overlapping osfs urls
tfeldmann 891d1fc
use OSFS instead of fs_open, rename cleanup_dst_on_error param
tfeldmann 4d80b22
fix `supports_rename` in `WrapReadOnly`
tfeldmann 78f6696
cleanup test_move filenames
tfeldmann 46fbc71
Fix circular import between `fs.base`, `fs.osfs` and `fs.move`
althonos 5675f84
cleanup and docs
tfeldmann da33922
test `src` first, then `dst`
tfeldmann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# mypy: ignore-errors | ||
try: | ||
from os.path import commonpath | ||
except ImportError: | ||
# Return the longest common sub-path of the sequence of paths given as input. | ||
# The paths are not normalized before comparing them (this is the | ||
# responsibility of the caller). Any trailing separator is stripped from the | ||
# returned path. | ||
|
||
def commonpath(paths): | ||
"""Given a sequence of path names, returns the longest common sub-path.""" | ||
|
||
if not paths: | ||
raise ValueError("commonpath() arg is an empty sequence") | ||
|
||
paths = tuple(paths) | ||
if isinstance(paths[0], bytes): | ||
sep = b"/" | ||
curdir = b"." | ||
else: | ||
sep = "/" | ||
curdir = "." | ||
|
||
split_paths = [path.split(sep) for path in paths] | ||
|
||
try: | ||
(isabs,) = set(p[:1] == sep for p in paths) | ||
except ValueError: | ||
raise ValueError("Can't mix absolute and relative paths") | ||
|
||
split_paths = [[c for c in s if c and c != curdir] for s in split_paths] | ||
s1 = min(split_paths) | ||
s2 = max(split_paths) | ||
common = s1 | ||
for i, c in enumerate(s1): | ||
if c != s2[i]: | ||
common = s1[:i] | ||
break | ||
|
||
prefix = sep if isabs else sep[:0] | ||
return prefix + sep.join(common) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,11 @@ | |
|
||
from .copy import copy_dir | ||
from .copy import copy_file | ||
from .errors import FSError | ||
from .opener import manage_fs | ||
from .osfs import OSFS | ||
from .path import frombase | ||
from ._pathcompat import commonpath | ||
|
||
if typing.TYPE_CHECKING: | ||
from .base import FS | ||
|
@@ -42,6 +46,7 @@ def move_file( | |
dst_fs, # type: Union[Text, FS] | ||
dst_path, # type: Text | ||
preserve_time=False, # type: bool | ||
cleanup_dst_on_error=True, # type: bool | ||
): | ||
# type: (...) -> None | ||
"""Move a file from one filesystem to another. | ||
|
@@ -53,26 +58,55 @@ def move_file( | |
dst_path (str): Path to a file on ``dst_fs``. | ||
preserve_time (bool): If `True`, try to preserve mtime of the | ||
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) as _src_fs: | ||
with manage_fs(dst_fs, create=True) as _dst_fs: | ||
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: | ||
# Same filesystem, may be optimized | ||
_src_fs.move( | ||
src_path, dst_path, overwrite=True, preserve_time=preserve_time | ||
) | ||
else: | ||
# Standard copy and delete | ||
with _src_fs.lock(), _dst_fs.lock(): | ||
copy_file( | ||
_src_fs, | ||
src_path, | ||
_dst_fs, | ||
dst_path, | ||
preserve_time=preserve_time, | ||
) | ||
return | ||
|
||
if _src_fs.hassyspath(src_path) and _dst_fs.hassyspath(dst_path): | ||
# if both filesystems have a syspath we create a new OSFS from a | ||
# common parent folder and use it to move the file. | ||
try: | ||
src_syspath = _src_fs.getsyspath(src_path) | ||
dst_syspath = _dst_fs.getsyspath(dst_path) | ||
common = commonpath([src_syspath, dst_syspath]) | ||
althonos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if common: | ||
rel_src = frombase(common, src_syspath) | ||
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) | ||
return # optimization worked, exit early | ||
except ValueError: | ||
# This is raised if we cannot find a common base folder. | ||
Comment on lines
+88
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this exception-catching still needed, now that you check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, because |
||
# In this case just fall through to the standard method. | ||
pass | ||
tfeldmann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Standard copy and delete | ||
with _src_fs.lock(), _dst_fs.lock(): | ||
copy_file( | ||
_src_fs, | ||
src_path, | ||
_dst_fs, | ||
dst_path, | ||
preserve_time=preserve_time, | ||
) | ||
try: | ||
_src_fs.remove(src_path) | ||
except FSError as e: | ||
# if the source cannot be removed we delete the copy on the | ||
# destination | ||
if cleanup_dst_on_error: | ||
_dst_fs.remove(dst_path) | ||
raise e | ||
|
||
|
||
def move_dir( | ||
|
@@ -97,22 +131,16 @@ def move_dir( | |
resources (defaults to `False`). | ||
|
||
""" | ||
|
||
def src(): | ||
return manage_fs(src_fs, writeable=False) | ||
|
||
def dst(): | ||
return manage_fs(dst_fs, create=True) | ||
|
||
with src() as _src_fs, dst() as _dst_fs: | ||
with _src_fs.lock(), _dst_fs.lock(): | ||
_dst_fs.makedir(dst_path, recreate=True) | ||
copy_dir( | ||
src_fs, | ||
src_path, | ||
dst_fs, | ||
dst_path, | ||
workers=workers, | ||
preserve_time=preserve_time, | ||
) | ||
_src_fs.removetree(src_path) | ||
with manage_fs(src_fs, writeable=True) as _src_fs: | ||
with manage_fs(dst_fs, writeable=True, create=True) as _dst_fs: | ||
with _src_fs.lock(), _dst_fs.lock(): | ||
_dst_fs.makedir(dst_path, recreate=True) | ||
copy_dir( | ||
src_fs, | ||
src_path, | ||
dst_fs, | ||
dst_path, | ||
workers=workers, | ||
preserve_time=preserve_time, | ||
) | ||
_src_fs.removetree(src_path) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.