@@ -405,13 +405,37 @@ def realpath(filename):
405405 bits = ['/' ] + filename .split ('/' )[1 :]
406406 for i in range (2 , len (bits )+ 1 ):
407407 component = join (* bits [0 :i ])
408- if islink (component ):
409- resolved = os .readlink (component )
410- (dir , file ) = split (component )
411- resolved = normpath (join (dir , resolved ))
412- newpath = join (* ([resolved ] + bits [i :]))
413- return realpath (newpath )
408+ # Resolve symbolic links.
409+ if islink (component ):
410+ resolved = _resolve_link (component )
411+ if resolved is None :
412+ # Infinite loop -- return original component + rest of the path
413+ return join (* ([component ] + bits [i :]))
414+ else :
415+ newpath = join (* ([resolved ] + bits [i :]))
416+ return realpath (newpath )
414417
415418 return filename
416419
420+
421+ def _resolve_link (path ):
422+ """Internal helper function. Takes a path and follows symlinks
423+ until we either arrive at something that isn't a symlink, or
424+ encounter a path we've seen before (meaning that there's a loop).
425+ """
426+ paths_seen = []
427+ while islink (path ):
428+ if path in paths_seen :
429+ # Already seen this path, so we must have a symlink loop
430+ return None
431+ paths_seen .append (path )
432+ # Resolve where the link points to
433+ resolved = os .readlink (path )
434+ if not abspath (resolved ):
435+ dir = dirname (path )
436+ path = normpath (join (dir , resolved ))
437+ else :
438+ path = normpath (resolved )
439+ return path
440+
417441supports_unicode_filenames = False
0 commit comments