88
99from .copy import copy_dir
1010from .copy import copy_file
11+ from .errors import FSError
1112from .opener import manage_fs
13+ from .osfs import OSFS
14+ from .path import frombase
15+ from ._pathcompat import commonpath
1216
1317if typing .TYPE_CHECKING :
1418 from .base import FS
@@ -42,6 +46,7 @@ def move_file(
4246 dst_fs , # type: Union[Text, FS]
4347 dst_path , # type: Text
4448 preserve_time = False , # type: bool
49+ cleanup_dst_on_error = True , # type: bool
4550):
4651 # type: (...) -> None
4752 """Move a file from one filesystem to another.
@@ -53,26 +58,55 @@ def move_file(
5358 dst_path (str): Path to a file on ``dst_fs``.
5459 preserve_time (bool): If `True`, try to preserve mtime of the
5560 resources (defaults to `False`).
61+ cleanup_dst_on_error (bool): If `True`, tries to delete the file copied to
62+ ``dst_fs`` if deleting the file from ``src_fs`` fails (defaults to `True`).
5663
5764 """
58- with manage_fs (src_fs ) as _src_fs :
59- with manage_fs (dst_fs , create = True ) as _dst_fs :
65+ with manage_fs (src_fs , writeable = True ) as _src_fs :
66+ with manage_fs (dst_fs , writeable = True , create = True ) as _dst_fs :
6067 if _src_fs is _dst_fs :
6168 # Same filesystem, may be optimized
6269 _src_fs .move (
6370 src_path , dst_path , overwrite = True , preserve_time = preserve_time
6471 )
65- else :
66- # Standard copy and delete
67- with _src_fs .lock (), _dst_fs .lock ():
68- copy_file (
69- _src_fs ,
70- src_path ,
71- _dst_fs ,
72- dst_path ,
73- preserve_time = preserve_time ,
74- )
72+ return
73+
74+ if _src_fs .hassyspath (src_path ) and _dst_fs .hassyspath (dst_path ):
75+ # if both filesystems have a syspath we create a new OSFS from a
76+ # common parent folder and use it to move the file.
77+ try :
78+ src_syspath = _src_fs .getsyspath (src_path )
79+ dst_syspath = _dst_fs .getsyspath (dst_path )
80+ common = commonpath ([src_syspath , dst_syspath ])
81+ if common :
82+ rel_src = frombase (common , src_syspath )
83+ rel_dst = frombase (common , dst_syspath )
84+ with _src_fs .lock (), _dst_fs .lock ():
85+ with OSFS (common ) as base :
86+ base .move (rel_src , rel_dst , preserve_time = preserve_time )
87+ return # optimization worked, exit early
88+ except ValueError :
89+ # This is raised if we cannot find a common base folder.
90+ # In this case just fall through to the standard method.
91+ pass
92+
93+ # Standard copy and delete
94+ with _src_fs .lock (), _dst_fs .lock ():
95+ copy_file (
96+ _src_fs ,
97+ src_path ,
98+ _dst_fs ,
99+ dst_path ,
100+ preserve_time = preserve_time ,
101+ )
102+ try :
75103 _src_fs .remove (src_path )
104+ except FSError as e :
105+ # if the source cannot be removed we delete the copy on the
106+ # destination
107+ if cleanup_dst_on_error :
108+ _dst_fs .remove (dst_path )
109+ raise e
76110
77111
78112def move_dir (
@@ -97,22 +131,16 @@ def move_dir(
97131 resources (defaults to `False`).
98132
99133 """
100-
101- def src ():
102- return manage_fs (src_fs , writeable = False )
103-
104- def dst ():
105- return manage_fs (dst_fs , create = True )
106-
107- with src () as _src_fs , dst () as _dst_fs :
108- with _src_fs .lock (), _dst_fs .lock ():
109- _dst_fs .makedir (dst_path , recreate = True )
110- copy_dir (
111- src_fs ,
112- src_path ,
113- dst_fs ,
114- dst_path ,
115- workers = workers ,
116- preserve_time = preserve_time ,
117- )
118- _src_fs .removetree (src_path )
134+ with manage_fs (src_fs , writeable = True ) as _src_fs :
135+ with manage_fs (dst_fs , writeable = True , create = True ) as _dst_fs :
136+ with _src_fs .lock (), _dst_fs .lock ():
137+ _dst_fs .makedir (dst_path , recreate = True )
138+ copy_dir (
139+ src_fs ,
140+ src_path ,
141+ dst_fs ,
142+ dst_path ,
143+ workers = workers ,
144+ preserve_time = preserve_time ,
145+ )
146+ _src_fs .removetree (src_path )
0 commit comments