15
15
logger = logging .getLogger ("fsspec.local" )
16
16
17
17
18
- def _remove_prefix (text : str , prefix : str ):
19
- if text .startswith (prefix ):
20
- return text [len (prefix ) :]
21
- return text
22
-
23
-
24
18
class LocalFileSystem (AbstractFileSystem ):
25
19
"""Interface to files on local storage
26
20
@@ -121,8 +115,8 @@ def lexists(self, path, **kwargs):
121
115
return osp .lexists (path )
122
116
123
117
def cp_file (self , path1 , path2 , ** kwargs ):
124
- path1 = self ._strip_protocol (path1 , remove_trailing_slash = True )
125
- path2 = self ._strip_protocol (path2 , remove_trailing_slash = True )
118
+ path1 = self ._strip_protocol (path1 )
119
+ path2 = self ._strip_protocol (path2 )
126
120
if self .auto_mkdir :
127
121
self .makedirs (self ._parent (path2 ), exist_ok = True )
128
122
if self .isfile (path1 ):
@@ -151,8 +145,8 @@ def put_file(self, path1, path2, callback=None, **kwargs):
151
145
return self .cp_file (path1 , path2 , ** kwargs )
152
146
153
147
def mv (self , path1 , path2 , ** kwargs ):
154
- path1 = self ._strip_protocol (path1 , remove_trailing_slash = True )
155
- path2 = self ._strip_protocol (path2 , remove_trailing_slash = True )
148
+ path1 = self ._strip_protocol (path1 )
149
+ path2 = self ._strip_protocol (path2 )
156
150
shutil .move (path1 , path2 )
157
151
158
152
def link (self , src , dst , ** kwargs ):
@@ -176,7 +170,7 @@ def rm(self, path, recursive=False, maxdepth=None):
176
170
path = [path ]
177
171
178
172
for p in path :
179
- p = self ._strip_protocol (p , remove_trailing_slash = True )
173
+ p = self ._strip_protocol (p )
180
174
if self .isdir (p ):
181
175
if not recursive :
182
176
raise ValueError ("Cannot delete directory, set recursive=True" )
@@ -219,7 +213,7 @@ def modified(self, path):
219
213
220
214
@classmethod
221
215
def _parent (cls , path ):
222
- path = cls ._strip_protocol (path , remove_trailing_slash = True )
216
+ path = cls ._strip_protocol (path )
223
217
if os .sep == "/" :
224
218
# posix native
225
219
return path .rsplit ("/" , 1 )[0 ] or "/"
@@ -234,17 +228,43 @@ def _parent(cls, path):
234
228
return path_
235
229
236
230
@classmethod
237
- def _strip_protocol (cls , path , remove_trailing_slash = False ):
231
+ def _strip_protocol (cls , path ):
238
232
path = stringify_path (path )
239
- if path .startswith ("file:" ):
240
- path = _remove_prefix (_remove_prefix (path , "file://" ), "file:" )
241
- if os .sep == "\\ " :
242
- path = path .lstrip ("/" )
233
+ if path .startswith ("file://" ):
234
+ path = path [7 :]
235
+ elif path .startswith ("file:" ):
236
+ path = path [5 :]
237
+ elif path .startswith ("local://" ):
238
+ path = path [8 :]
243
239
elif path .startswith ("local:" ):
244
- path = _remove_prefix (_remove_prefix (path , "local://" ), "local:" )
245
- if os .sep == "\\ " :
246
- path = path .lstrip ("/" )
247
- return make_path_posix (path , remove_trailing_slash )
240
+ path = path [6 :]
241
+
242
+ path = make_path_posix (path )
243
+ if os .sep != "/" :
244
+ # This code-path is a stripped down version of
245
+ # > drive, path = ntpath.splitdrive(path)
246
+ if path [1 :2 ] == ":" :
247
+ # Absolute drive-letter path, e.g. X:\Windows
248
+ # Relative path with drive, e.g. X:Windows
249
+ drive , path = path [:2 ], path [2 :]
250
+ elif path [:2 ] == "//" :
251
+ # UNC drives, e.g. \\server\share or \\?\UNC\server\share
252
+ # Device drives, e.g. \\.\device or \\?\device
253
+ if (index1 := path .find ("/" , 2 )) == - 1 or (
254
+ index2 := path .find ("/" , index1 + 1 )
255
+ ) == - 1 :
256
+ drive , path = path , ""
257
+ else :
258
+ drive , path = path [:index2 ], path [index2 :]
259
+ else :
260
+ # Relative path, e.g. Windows
261
+ drive = ""
262
+
263
+ path = path .rstrip ("/" ) or cls .root_marker
264
+ return drive + path
265
+
266
+ else :
267
+ return path .rstrip ("/" ) or cls .root_marker
248
268
249
269
def _isfilestore (self ):
250
270
# Inheriting from DaskFileSystem makes this False (S3, etc. were)
@@ -257,42 +277,55 @@ def chmod(self, path, mode):
257
277
return os .chmod (path , mode )
258
278
259
279
260
- def make_path_posix (path , remove_trailing_slash = False ):
261
- """Make path generic for current OS"""
280
+ def make_path_posix (path ):
281
+ """Make path generic and absolute for current OS"""
262
282
if not isinstance (path , str ):
263
283
if isinstance (path , (list , set , tuple )):
264
- return type (path )(make_path_posix (p , remove_trailing_slash ) for p in path )
284
+ return type (path )(make_path_posix (p ) for p in path )
265
285
else :
266
- path = str (stringify_path (path ))
286
+ path = stringify_path (path )
287
+ if not isinstance (path , str ):
288
+ raise TypeError (f"could not convert { path !r} to string" )
267
289
if os .sep == "/" :
268
290
# Native posix
269
291
if path .startswith ("/" ):
270
292
# most common fast case for posix
271
- return path . rstrip ( "/" ) or "/" if remove_trailing_slash else path
293
+ return path
272
294
elif path .startswith ("~" ):
273
- return make_path_posix ( osp .expanduser (path ), remove_trailing_slash )
295
+ return osp .expanduser (path )
274
296
elif path .startswith ("./" ):
275
297
path = path [2 :]
276
- path = f" { os . getcwd () } / { path } "
277
- return path . rstrip ( "/" ) or "/" if remove_trailing_slash else path
298
+ elif path == "." :
299
+ path = ""
278
300
return f"{ os .getcwd ()} /{ path } "
279
301
else :
280
302
# NT handling
281
- if len (path ) > 1 :
282
- if path [1 ] == ":" :
283
- # windows full path like "C:\\local\\path"
284
- if len (path ) <= 3 :
285
- # nt root (something like c:/)
286
- return path [0 ] + ":/"
287
- path = path .replace ("\\ " , "/" ).replace ("//" , "/" )
288
- return path .rstrip ("/" ) if remove_trailing_slash else path
289
- elif path [0 ] == "~" :
290
- return make_path_posix (osp .expanduser (path ), remove_trailing_slash )
291
- elif path .startswith (("\\ \\ " , "//" )):
292
- # windows UNC/DFS-style paths
293
- path = "//" + path [2 :].replace ("\\ " , "/" ).replace ("//" , "/" )
294
- return path .rstrip ("/" ) if remove_trailing_slash else path
295
- return make_path_posix (osp .abspath (path ), remove_trailing_slash )
303
+ if path [0 :1 ] == "/" and path [2 :3 ] == ":" :
304
+ # path is like "/c:/local/path"
305
+ path = path [1 :]
306
+ if path [1 :2 ] == ":" :
307
+ # windows full path like "C:\\local\\path"
308
+ if len (path ) <= 3 :
309
+ # nt root (something like c:/)
310
+ return path [0 ] + ":/"
311
+ path = path .replace ("\\ " , "/" )
312
+ return path
313
+ elif path [0 :1 ] == "~" :
314
+ return make_path_posix (osp .expanduser (path ))
315
+ elif path .startswith (("\\ \\ " , "//" )):
316
+ # windows UNC/DFS-style paths
317
+ return "//" + path [2 :].replace ("\\ " , "/" )
318
+ elif path .startswith (("\\ " , "/" )):
319
+ # windows relative path with root
320
+ path = path .replace ("\\ " , "/" )
321
+ return f"{ osp .splitdrive (os .getcwd ())[0 ]} { path } "
322
+ else :
323
+ path = path .replace ("\\ " , "/" )
324
+ if path .startswith ("./" ):
325
+ path = path [2 :]
326
+ elif path == "." :
327
+ path = ""
328
+ return f"{ make_path_posix (os .getcwd ())} /{ path } "
296
329
297
330
298
331
def trailing_sep (path ):
0 commit comments