3
3
import logging
4
4
import os
5
5
import os .path as osp
6
- import re
7
6
import shutil
8
7
import stat
9
8
import tempfile
16
15
logger = logging .getLogger ("fsspec.local" )
17
16
18
17
18
+ def _remove_prefix (text : str , prefix : str ):
19
+ if text .startswith (prefix ):
20
+ return text [len (prefix ) :]
21
+ return text
22
+
23
+
19
24
class LocalFileSystem (AbstractFileSystem ):
20
25
"""Interface to files on local storage
21
26
@@ -116,8 +121,8 @@ def lexists(self, path, **kwargs):
116
121
return osp .lexists (path )
117
122
118
123
def cp_file (self , path1 , path2 , ** kwargs ):
119
- path1 = self ._strip_protocol (path1 ). rstrip ( "/" )
120
- path2 = self ._strip_protocol (path2 ). rstrip ( "/" )
124
+ path1 = self ._strip_protocol (path1 , remove_trailing_slash = True )
125
+ path2 = self ._strip_protocol (path2 , remove_trailing_slash = True )
121
126
if self .auto_mkdir :
122
127
self .makedirs (self ._parent (path2 ), exist_ok = True )
123
128
if self .isfile (path1 ):
@@ -138,8 +143,8 @@ def put_file(self, path1, path2, callback=None, **kwargs):
138
143
return self .cp_file (path1 , path2 , ** kwargs )
139
144
140
145
def mv_file (self , path1 , path2 , ** kwargs ):
141
- path1 = self ._strip_protocol (path1 ). rstrip ( "/" )
142
- path2 = self ._strip_protocol (path2 ). rstrip ( "/" )
146
+ path1 = self ._strip_protocol (path1 , remove_trailing_slash = True )
147
+ path2 = self ._strip_protocol (path2 , remove_trailing_slash = True )
143
148
shutil .move (path1 , path2 )
144
149
145
150
def link (self , src , dst , ** kwargs ):
@@ -163,7 +168,7 @@ def rm(self, path, recursive=False, maxdepth=None):
163
168
path = [path ]
164
169
165
170
for p in path :
166
- p = self ._strip_protocol (p ). rstrip ( "/" )
171
+ p = self ._strip_protocol (p , remove_trailing_slash = True )
167
172
if self .isdir (p ):
168
173
if not recursive :
169
174
raise ValueError ("Cannot delete directory, set recursive=True" )
@@ -206,24 +211,32 @@ def modified(self, path):
206
211
207
212
@classmethod
208
213
def _parent (cls , path ):
209
- path = cls ._strip_protocol (path ).rstrip ("/" )
210
- if "/" in path :
211
- return path .rsplit ("/" , 1 )[0 ]
214
+ path = cls ._strip_protocol (path , remove_trailing_slash = True )
215
+ if os .sep == "/" :
216
+ # posix native
217
+ return path .rsplit ("/" , 1 )[0 ] or "/"
212
218
else :
213
- return cls .root_marker
219
+ # NT
220
+ path_ = path .rsplit ("/" , 1 )[0 ]
221
+ if len (path_ ) <= 3 :
222
+ if path_ [1 :2 ] == ":" :
223
+ # nt root (something like c:/)
224
+ return path_ [0 ] + ":/"
225
+ # More cases may be required here
226
+ return path_
214
227
215
228
@classmethod
216
- def _strip_protocol (cls , path ):
229
+ def _strip_protocol (cls , path , remove_trailing_slash = False ):
217
230
path = stringify_path (path )
218
- if path .startswith ("file://" ):
219
- path = path [7 :]
220
- elif path .startswith ("file:" ):
221
- path = path [5 :]
222
- elif path .startswith ("local://" ):
223
- path = path [8 :]
231
+ if path .startswith ("file:" ):
232
+ path = _remove_prefix (_remove_prefix (path , "file://" ), "file:" )
233
+ if os .sep == "\\ " :
234
+ path = path .lstrip ("/" )
224
235
elif path .startswith ("local:" ):
225
- path = path [6 :]
226
- return make_path_posix (path ).rstrip ("/" ) or cls .root_marker
236
+ path = _remove_prefix (_remove_prefix (path , "local://" ), "local:" )
237
+ if os .sep == "\\ " :
238
+ path = path .lstrip ("/" )
239
+ return make_path_posix (path , remove_trailing_slash )
227
240
228
241
def _isfilestore (self ):
229
242
# Inheriting from DaskFileSystem makes this False (S3, etc. were)
@@ -236,47 +249,42 @@ def chmod(self, path, mode):
236
249
return os .chmod (path , mode )
237
250
238
251
239
- def make_path_posix (path , sep = os .sep ):
240
- """Make path generic"""
241
- if isinstance (path , (list , set , tuple )):
242
- return type (path )(make_path_posix (p ) for p in path )
243
- if "~" in path :
244
- path = osp .expanduser (path )
245
- if sep == "/" :
246
- # most common fast case for posix
252
+ def make_path_posix (path , remove_trailing_slash = False ):
253
+ """Make path generic for current OS"""
254
+ if not isinstance (path , str ):
255
+ if isinstance (path , (list , set , tuple )):
256
+ return type (path )(make_path_posix (p , remove_trailing_slash ) for p in path )
257
+ else :
258
+ path = str (stringify_path (path ))
259
+ if os .sep == "/" :
260
+ # Native posix
247
261
if path .startswith ("/" ):
248
- return path
249
- if path .startswith ("./" ):
262
+ # most common fast case for posix
263
+ return path .rstrip ("/" ) or "/" if remove_trailing_slash else path
264
+ elif path .startswith ("~" ):
265
+ return make_path_posix (osp .expanduser (path ), remove_trailing_slash )
266
+ elif path .startswith ("./" ):
250
267
path = path [2 :]
268
+ path = f"{ os .getcwd ()} /{ path } "
269
+ return path .rstrip ("/" ) or "/" if remove_trailing_slash else path
251
270
return f"{ os .getcwd ()} /{ path } "
252
- if (
253
- (sep not in path and "/" not in path )
254
- or (sep == "/" and not path .startswith ("/" ))
255
- or (sep == "\\ " and ":" not in path and not path .startswith ("\\ \\ " ))
256
- ):
257
- # relative path like "path" or "rel\\path" (win) or rel/path"
258
- if os .sep == "\\ " :
259
- # abspath made some more '\\' separators
260
- return make_path_posix (osp .abspath (path ))
261
- else :
262
- return f"{ os .getcwd ()} /{ path } "
263
- if path .startswith ("file://" ):
264
- path = path [7 :]
265
- if re .match ("/[A-Za-z]:" , path ):
266
- # for windows file URI like "file:///C:/folder/file"
267
- # or "file:///C:\\dir\\file"
268
- path = path [1 :].replace ("\\ " , "/" ).replace ("//" , "/" )
269
- if path .startswith ("\\ \\ " ):
270
- # special case for windows UNC/DFS-style paths, do nothing,
271
- # just flip the slashes around (case below does not work!)
272
- return path .replace ("\\ " , "/" )
273
- if re .match ("[A-Za-z]:" , path ):
274
- # windows full path like "C:\\local\\path"
275
- return path .lstrip ("\\ " ).replace ("\\ " , "/" ).replace ("//" , "/" )
276
- if path .startswith ("\\ " ):
277
- # windows network path like "\\server\\path"
278
- return "/" + path .lstrip ("\\ " ).replace ("\\ " , "/" ).replace ("//" , "/" )
279
- return path
271
+ else :
272
+ # NT handling
273
+ if len (path ) > 1 :
274
+ if path [1 ] == ":" :
275
+ # windows full path like "C:\\local\\path"
276
+ if len (path ) <= 3 :
277
+ # nt root (something like c:/)
278
+ return path [0 ] + ":/"
279
+ path = path .replace ("\\ " , "/" ).replace ("//" , "/" )
280
+ return path .rstrip ("/" ) if remove_trailing_slash else path
281
+ elif path [0 ] == "~" :
282
+ return make_path_posix (osp .expanduser (path ), remove_trailing_slash )
283
+ elif path .startswith (("\\ \\ " , "//" )):
284
+ # windows UNC/DFS-style paths
285
+ path = "//" + path [2 :].replace ("\\ " , "/" ).replace ("//" , "/" )
286
+ return path .rstrip ("/" ) if remove_trailing_slash else path
287
+ return make_path_posix (osp .abspath (path ), remove_trailing_slash )
280
288
281
289
282
290
def trailing_sep (path ):
0 commit comments