14
14
15
15
16
16
supports_symlinks = True
17
- if os .name == 'nt' :
18
- import nt
19
- if sys .getwindowsversion ()[:2 ] >= (6 , 0 ):
20
- from nt import _getfinalpathname
21
- else :
22
- supports_symlinks = False
23
- _getfinalpathname = None
24
- else :
25
- nt = None
26
17
27
18
28
19
__all__ = [
34
25
# Internals
35
26
#
36
27
28
+ _WINERROR_NOT_READY = 21 # drive exists but is not accessible
29
+ _WINERROR_INVALID_NAME = 123 # fix for bpo-35306
30
+ _WINERROR_CANT_RESOLVE_FILENAME = 1921 # broken symlink pointing to itself
31
+
37
32
# EBADF - guard against macOS `stat` throwing EBADF
38
33
_IGNORED_ERROS = (ENOENT , ENOTDIR , EBADF , ELOOP )
39
34
40
35
_IGNORED_WINERRORS = (
41
- 21 , # ERROR_NOT_READY - drive exists but is not accessible
42
- 123 , # ERROR_INVALID_NAME - fix for bpo-35306
43
- 1921 , # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself
44
- )
36
+ _WINERROR_NOT_READY ,
37
+ _WINERROR_INVALID_NAME ,
38
+ _WINERROR_CANT_RESOLVE_FILENAME )
45
39
46
40
def _ignore_error (exception ):
47
41
return (getattr (exception , 'errno' , None ) in _IGNORED_ERROS or
@@ -200,30 +194,6 @@ def casefold_parts(self, parts):
200
194
def compile_pattern (self , pattern ):
201
195
return re .compile (fnmatch .translate (pattern ), re .IGNORECASE ).fullmatch
202
196
203
- def resolve (self , path , strict = False ):
204
- s = str (path )
205
- if not s :
206
- return os .getcwd ()
207
- previous_s = None
208
- if _getfinalpathname is not None :
209
- if strict :
210
- return self ._ext_to_normal (_getfinalpathname (s ))
211
- else :
212
- tail_parts = [] # End of the path after the first one not found
213
- while True :
214
- try :
215
- s = self ._ext_to_normal (_getfinalpathname (s ))
216
- except FileNotFoundError :
217
- previous_s = s
218
- s , tail = os .path .split (s )
219
- tail_parts .append (tail )
220
- if previous_s == s :
221
- return path
222
- else :
223
- return os .path .join (s , * reversed (tail_parts ))
224
- # Means fallback on absolute
225
- return None
226
-
227
197
def _split_extended_path (self , s , ext_prefix = ext_namespace_prefix ):
228
198
prefix = ''
229
199
if s .startswith (ext_prefix ):
@@ -234,10 +204,6 @@ def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
234
204
s = '\\ ' + s [3 :]
235
205
return prefix , s
236
206
237
- def _ext_to_normal (self , s ):
238
- # Turn back an extended path into a normal DOS-like path
239
- return self ._split_extended_path (s )[1 ]
240
-
241
207
def is_reserved (self , parts ):
242
208
# NOTE: the rules for reserved names seem somewhat complicated
243
209
# (e.g. r"..\NUL" is reserved but not r"foo\NUL" if "foo" does not
@@ -324,54 +290,6 @@ def casefold_parts(self, parts):
324
290
def compile_pattern (self , pattern ):
325
291
return re .compile (fnmatch .translate (pattern )).fullmatch
326
292
327
- def resolve (self , path , strict = False ):
328
- sep = self .sep
329
- accessor = path ._accessor
330
- seen = {}
331
- def _resolve (path , rest ):
332
- if rest .startswith (sep ):
333
- path = ''
334
-
335
- for name in rest .split (sep ):
336
- if not name or name == '.' :
337
- # current dir
338
- continue
339
- if name == '..' :
340
- # parent dir
341
- path , _ , _ = path .rpartition (sep )
342
- continue
343
- if path .endswith (sep ):
344
- newpath = path + name
345
- else :
346
- newpath = path + sep + name
347
- if newpath in seen :
348
- # Already seen this path
349
- path = seen [newpath ]
350
- if path is not None :
351
- # use cached value
352
- continue
353
- # The symlink is not resolved, so we must have a symlink loop.
354
- raise RuntimeError ("Symlink loop from %r" % newpath )
355
- # Resolve the symbolic link
356
- try :
357
- target = accessor .readlink (newpath )
358
- except OSError as e :
359
- if e .errno != EINVAL and strict :
360
- raise
361
- # Not a symlink, or non-strict mode. We just leave the path
362
- # untouched.
363
- path = newpath
364
- else :
365
- seen [newpath ] = None # not resolved symlink
366
- path = _resolve (path , target )
367
- seen [newpath ] = path # resolved symlink
368
-
369
- return path
370
- # NOTE: according to POSIX, getcwd() cannot contain path components
371
- # which are symlinks.
372
- base = '' if path .is_absolute () else os .getcwd ()
373
- return _resolve (base , str (path )) or sep
374
-
375
293
def is_reserved (self , parts ):
376
294
return False
377
295
@@ -443,17 +361,11 @@ def link_to(self, target):
443
361
444
362
replace = os .replace
445
363
446
- if nt :
447
- if supports_symlinks :
448
- symlink = os .symlink
449
- else :
450
- def symlink (a , b , target_is_directory ):
451
- raise NotImplementedError ("symlink() not available on this system" )
364
+ if hasattr (os , "symlink" ):
365
+ symlink = os .symlink
452
366
else :
453
- # Under POSIX, os.symlink() takes two args
454
- @staticmethod
455
- def symlink (a , b , target_is_directory ):
456
- return os .symlink (a , b )
367
+ def symlink (self , src , dst , target_is_directory = False ):
368
+ raise NotImplementedError ("os.symlink() not available on this system" )
457
369
458
370
utime = os .utime
459
371
@@ -475,6 +387,12 @@ def group(self, path):
475
387
except ImportError :
476
388
raise NotImplementedError ("Path.group() is unsupported on this system" )
477
389
390
+ getcwd = os .getcwd
391
+
392
+ expanduser = staticmethod (os .path .expanduser )
393
+
394
+ realpath = staticmethod (os .path .realpath )
395
+
478
396
479
397
_normal_accessor = _NormalAccessor ()
480
398
@@ -1212,17 +1130,27 @@ def resolve(self, strict=False):
1212
1130
normalizing it (for example turning slashes into backslashes under
1213
1131
Windows).
1214
1132
"""
1215
- s = self ._flavour .resolve (self , strict = strict )
1216
- if s is None :
1217
- # No symlink resolution => for consistency, raise an error if
1218
- # the path doesn't exist or is forbidden
1219
- self .stat ()
1220
- s = str (self .absolute ())
1221
- # Now we have no symlinks in the path, it's safe to normalize it.
1222
- normed = self ._flavour .pathmod .normpath (s )
1223
- obj = self ._from_parts ((normed ,), init = False )
1224
- obj ._init (template = self )
1225
- return obj
1133
+
1134
+ def check_eloop (e ):
1135
+ winerror = getattr (e , 'winerror' , 0 )
1136
+ if e .errno == ELOOP or winerror == _WINERROR_CANT_RESOLVE_FILENAME :
1137
+ raise RuntimeError ("Symlink loop from %r" % e .filename )
1138
+
1139
+ try :
1140
+ s = self ._accessor .realpath (self , strict = strict )
1141
+ except OSError as e :
1142
+ check_eloop (e )
1143
+ raise
1144
+ p = self ._from_parts ((s ,))
1145
+
1146
+ # In non-strict mode, realpath() doesn't raise on symlink loops.
1147
+ # Ensure we get an exception by calling stat()
1148
+ if not strict :
1149
+ try :
1150
+ p .stat ()
1151
+ except OSError as e :
1152
+ check_eloop (e )
1153
+ return p
1226
1154
1227
1155
def stat (self ):
1228
1156
"""
0 commit comments