1414
1515
1616supports_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
2617
2718
2819__all__ = [
3425# Internals
3526#
3627
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+
3732# EBADF - guard against macOS `stat` throwing EBADF
3833_IGNORED_ERROS = (ENOENT , ENOTDIR , EBADF , ELOOP )
3934
4035_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 )
4539
4640def _ignore_error (exception ):
4741 return (getattr (exception , 'errno' , None ) in _IGNORED_ERROS or
@@ -200,30 +194,6 @@ def casefold_parts(self, parts):
200194 def compile_pattern (self , pattern ):
201195 return re .compile (fnmatch .translate (pattern ), re .IGNORECASE ).fullmatch
202196
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-
227197 def _split_extended_path (self , s , ext_prefix = ext_namespace_prefix ):
228198 prefix = ''
229199 if s .startswith (ext_prefix ):
@@ -234,10 +204,6 @@ def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
234204 s = '\\ ' + s [3 :]
235205 return prefix , s
236206
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-
241207 def is_reserved (self , parts ):
242208 # NOTE: the rules for reserved names seem somewhat complicated
243209 # (e.g. r"..\NUL" is reserved but not r"foo\NUL" if "foo" does not
@@ -324,54 +290,6 @@ def casefold_parts(self, parts):
324290 def compile_pattern (self , pattern ):
325291 return re .compile (fnmatch .translate (pattern )).fullmatch
326292
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-
375293 def is_reserved (self , parts ):
376294 return False
377295
@@ -443,17 +361,11 @@ def link_to(self, target):
443361
444362 replace = os .replace
445363
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
452366 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" )
457369
458370 utime = os .utime
459371
@@ -475,6 +387,12 @@ def group(self, path):
475387 except ImportError :
476388 raise NotImplementedError ("Path.group() is unsupported on this system" )
477389
390+ getcwd = os .getcwd
391+
392+ expanduser = staticmethod (os .path .expanduser )
393+
394+ realpath = staticmethod (os .path .realpath )
395+
478396
479397_normal_accessor = _NormalAccessor ()
480398
@@ -1212,17 +1130,27 @@ def resolve(self, strict=False):
12121130 normalizing it (for example turning slashes into backslashes under
12131131 Windows).
12141132 """
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
12261154
12271155 def stat (self ):
12281156 """
0 commit comments