1515logger = logging .getLogger ("fsspec.local" )
1616
1717
18- def _remove_prefix (text : str , prefix : str ):
19- if text .startswith (prefix ):
20- return text [len (prefix ) :]
21- return text
22-
23-
2418class LocalFileSystem (AbstractFileSystem ):
2519 """Interface to files on local storage
2620
@@ -121,8 +115,8 @@ def lexists(self, path, **kwargs):
121115 return osp .lexists (path )
122116
123117 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 )
126120 if self .auto_mkdir :
127121 self .makedirs (self ._parent (path2 ), exist_ok = True )
128122 if self .isfile (path1 ):
@@ -151,8 +145,8 @@ def put_file(self, path1, path2, callback=None, **kwargs):
151145 return self .cp_file (path1 , path2 , ** kwargs )
152146
153147 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 )
156150 shutil .move (path1 , path2 )
157151
158152 def link (self , src , dst , ** kwargs ):
@@ -176,7 +170,7 @@ def rm(self, path, recursive=False, maxdepth=None):
176170 path = [path ]
177171
178172 for p in path :
179- p = self ._strip_protocol (p , remove_trailing_slash = True )
173+ p = self ._strip_protocol (p )
180174 if self .isdir (p ):
181175 if not recursive :
182176 raise ValueError ("Cannot delete directory, set recursive=True" )
@@ -219,7 +213,7 @@ def modified(self, path):
219213
220214 @classmethod
221215 def _parent (cls , path ):
222- path = cls ._strip_protocol (path , remove_trailing_slash = True )
216+ path = cls ._strip_protocol (path )
223217 if os .sep == "/" :
224218 # posix native
225219 return path .rsplit ("/" , 1 )[0 ] or "/"
@@ -234,17 +228,43 @@ def _parent(cls, path):
234228 return path_
235229
236230 @classmethod
237- def _strip_protocol (cls , path , remove_trailing_slash = False ):
231+ def _strip_protocol (cls , path ):
238232 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 :]
243239 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
248268
249269 def _isfilestore (self ):
250270 # Inheriting from DaskFileSystem makes this False (S3, etc. were)
@@ -257,42 +277,55 @@ def chmod(self, path, mode):
257277 return os .chmod (path , mode )
258278
259279
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"""
262282 if not isinstance (path , str ):
263283 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 )
265285 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" )
267289 if os .sep == "/" :
268290 # Native posix
269291 if path .startswith ("/" ):
270292 # most common fast case for posix
271- return path . rstrip ( "/" ) or "/" if remove_trailing_slash else path
293+ return path
272294 elif path .startswith ("~" ):
273- return make_path_posix ( osp .expanduser (path ), remove_trailing_slash )
295+ return osp .expanduser (path )
274296 elif path .startswith ("./" ):
275297 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 = ""
278300 return f"{ os .getcwd ()} /{ path } "
279301 else :
280302 # 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 } "
296329
297330
298331def trailing_sep (path ):
0 commit comments