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
@@ -475,6 +393,15 @@ def group(self, path):
475393 except ImportError :
476394 raise NotImplementedError ("Path.group() is unsupported on this system" )
477395
396+ < << << << HEAD
397+ == == == =
398+ getcwd = os .getcwd
399+
400+ expanduser = staticmethod (os .path .expanduser )
401+
402+ realpath = staticmethod (os .path .realpath )
403+
404+ >> >> >> > baecfbd849d (bpo - 43757 : Make pathlib use os .path .realpath () to resolve symlinks in a path (GH - 25264 ))
478405
479406_normal_accessor = _NormalAccessor ()
480407
@@ -1212,6 +1139,7 @@ def resolve(self, strict=False):
12121139 normalizing it (for example turning slashes into backslashes under
12131140 Windows).
12141141 """
1142+ < << << << HEAD
12151143 s = self ._flavour .resolve (self , strict = strict )
12161144 if s is None :
12171145 # No symlink resolution => for consistency, raise an error if
@@ -1223,6 +1151,29 @@ def resolve(self, strict=False):
12231151 obj = self ._from_parts ((normed ,), init = False )
12241152 obj ._init (template = self )
12251153 return obj
1154+ == == == =
1155+
1156+ def check_eloop (e ):
1157+ winerror = getattr (e , 'winerror' , 0 )
1158+ if e .errno == ELOOP or winerror == _WINERROR_CANT_RESOLVE_FILENAME :
1159+ raise RuntimeError ("Symlink loop from %r" % e .filename )
1160+
1161+ try :
1162+ s = self ._accessor .realpath (self , strict = strict )
1163+ except OSError as e :
1164+ check_eloop (e )
1165+ raise
1166+ p = self ._from_parts ((s ,))
1167+
1168+ # In non-strict mode, realpath() doesn't raise on symlink loops.
1169+ # Ensure we get an exception by calling stat()
1170+ if not strict :
1171+ try :
1172+ p .stat ()
1173+ except OSError as e :
1174+ check_eloop (e )
1175+ return p
1176+ >> >> >> > baecfbd849d (bpo - 43757 : Make pathlib use os .path .realpath () to resolve symlinks in a path (GH - 25264 ))
12261177
12271178 def stat (self ):
12281179 """
0 commit comments