5
5
6
6
import typing
7
7
8
+ import os
9
+
8
10
from ._pathcompat import commonpath
9
11
from .copy import copy_dir , copy_file
10
- from .errors import FSError
12
+ from .error_tools import convert_os_errors
13
+ from .errors import DirectoryExpected , FSError , IllegalDestination , ResourceNotFound
11
14
from .opener import manage_fs
12
15
from .osfs import OSFS
13
- from .path import frombase
16
+ from .path import frombase , isbase
14
17
15
18
if typing .TYPE_CHECKING :
16
19
from typing import Text , Union
@@ -26,15 +29,13 @@ def move_fs(
26
29
):
27
30
# type: (...) -> None
28
31
"""Move the contents of a filesystem to another filesystem.
29
-
30
32
Arguments:
31
33
src_fs (FS or str): Source filesystem (instance or URL).
32
34
dst_fs (FS or str): Destination filesystem (instance or URL).
33
35
workers (int): Use `worker` threads to copy data, or ``0`` (default) for
34
36
a single-threaded copy.
35
37
preserve_time (bool): If `True`, try to preserve mtime of the
36
38
resources (defaults to `False`).
37
-
38
39
"""
39
40
move_dir (src_fs , "/" , dst_fs , "/" , workers = workers , preserve_time = preserve_time )
40
41
@@ -49,7 +50,6 @@ def move_file(
49
50
):
50
51
# type: (...) -> None
51
52
"""Move a file from one filesystem to another.
52
-
53
53
Arguments:
54
54
src_fs (FS or str): Source filesystem (instance or URL).
55
55
src_path (str): Path to a file on ``src_fs``.
@@ -59,7 +59,6 @@ def move_file(
59
59
resources (defaults to `False`).
60
60
cleanup_dst_on_error (bool): If `True`, tries to delete the file copied to
61
61
``dst_fs`` if deleting the file from ``src_fs`` fails (defaults to `True`).
62
-
63
62
"""
64
63
with manage_fs (src_fs , writeable = True ) as _src_fs :
65
64
with manage_fs (dst_fs , writeable = True , create = True ) as _dst_fs :
@@ -123,7 +122,6 @@ def move_dir(
123
122
):
124
123
# type: (...) -> None
125
124
"""Move a directory from one filesystem to another.
126
-
127
125
Arguments:
128
126
src_fs (FS or str): Source filesystem (instance or URL).
129
127
src_path (str): Path to a directory on ``src_fs``
@@ -133,10 +131,35 @@ def move_dir(
133
131
(default) for a single-threaded copy.
134
132
preserve_time (bool): If `True`, try to preserve mtime of the
135
133
resources (defaults to `False`).
136
-
134
+ Raises:
135
+ fs.errors.ResourceNotFound: if ``src_path`` does not exist on `src_fs`
136
+ fs.errors.DirectoryExpected: if ``src_path`` or one of its
137
+ ancestors is not a directory.
138
+ fs.errors.IllegalDestination: when moving a folder into itself
137
139
"""
138
140
with manage_fs (src_fs , writeable = True ) as _src_fs :
139
141
with manage_fs (dst_fs , writeable = True , create = True ) as _dst_fs :
142
+ if not _src_fs .exists (src_path ):
143
+ raise ResourceNotFound (src_path )
144
+ if not _src_fs .isdir (src_path ):
145
+ raise DirectoryExpected (src_path )
146
+
147
+ # if both filesystems have a syspath we use `os.rename` to move the folder
148
+ if _src_fs .hassyspath (src_path ) and _dst_fs .hassyspath (dst_path ):
149
+ src_syspath = _src_fs .getsyspath (src_path )
150
+ dst_syspath = _dst_fs .getsyspath (dst_path )
151
+ # recheck if the move operation is legal using the syspaths
152
+ if isbase (src_syspath , dst_syspath ):
153
+ raise IllegalDestination (dst_path )
154
+ with _src_fs .lock (), _dst_fs .lock ():
155
+ with convert_os_errors ("move_dir" , src_path , directory = True ):
156
+ os .rename (src_syspath , dst_syspath )
157
+ # recreate the root dir if it has been renamed
158
+ if src_path == "/" :
159
+ _src_fs .makedir ("/" )
160
+ return # optimization worked, exit early
161
+
162
+ # standard copy then delete
140
163
with _src_fs .lock (), _dst_fs .lock ():
141
164
_dst_fs .makedir (dst_path , recreate = True )
142
165
copy_dir (
0 commit comments