8
8
9
9
from .copy import copy_dir
10
10
from .copy import copy_file
11
+ from .errors import FSError
11
12
from .opener import manage_fs
13
+ from .osfs import OSFS
14
+ from .path import frombase
15
+ from ._pathcompat import commonpath
12
16
13
17
if typing .TYPE_CHECKING :
14
18
from .base import FS
@@ -42,6 +46,7 @@ def move_file(
42
46
dst_fs , # type: Union[Text, FS]
43
47
dst_path , # type: Text
44
48
preserve_time = False , # type: bool
49
+ cleanup_dst_on_error = True , # type: bool
45
50
):
46
51
# type: (...) -> None
47
52
"""Move a file from one filesystem to another.
@@ -53,26 +58,55 @@ def move_file(
53
58
dst_path (str): Path to a file on ``dst_fs``.
54
59
preserve_time (bool): If `True`, try to preserve mtime of the
55
60
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`).
56
63
57
64
"""
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 :
60
67
if _src_fs is _dst_fs :
61
68
# Same filesystem, may be optimized
62
69
_src_fs .move (
63
70
src_path , dst_path , overwrite = True , preserve_time = preserve_time
64
71
)
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 :
75
103
_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
76
110
77
111
78
112
def move_dir (
@@ -97,22 +131,16 @@ def move_dir(
97
131
resources (defaults to `False`).
98
132
99
133
"""
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