2222altsep = None
2323devnull = '/dev/null'
2424
25+ import errno
2526import os
2627import sys
2728import stat
@@ -408,6 +409,10 @@ def realpath(filename, *, strict=False):
408409 # very fast way of spelling list(reversed(...)).
409410 rest = filename .split (sep )[::- 1 ]
410411
412+ # Number of unprocessed parts in 'rest'. This can differ from len(rest)
413+ # later, because 'rest' might contain markers for unresolved symlinks.
414+ part_count = len (rest )
415+
411416 # The resolved path, which is absolute throughout this function.
412417 # Note: getcwd() returns a normalized and symlink-free path.
413418 path = sep if filename .startswith (sep ) else getcwd ()
@@ -418,12 +423,13 @@ def realpath(filename, *, strict=False):
418423 # the same links.
419424 seen = {}
420425
421- while rest :
426+ while part_count :
422427 name = rest .pop ()
423428 if name is None :
424429 # resolved symlink target
425430 seen [rest .pop ()] = path
426431 continue
432+ part_count -= 1
427433 if not name or name == curdir :
428434 # current dir
429435 continue
@@ -436,8 +442,11 @@ def realpath(filename, *, strict=False):
436442 else :
437443 newpath = path + sep + name
438444 try :
439- st = os .lstat (newpath )
440- if not stat .S_ISLNK (st .st_mode ):
445+ st_mode = os .lstat (newpath ).st_mode
446+ if not stat .S_ISLNK (st_mode ):
447+ if strict and part_count and not stat .S_ISDIR (st_mode ):
448+ raise OSError (errno .ENOTDIR , os .strerror (errno .ENOTDIR ),
449+ newpath )
441450 path = newpath
442451 continue
443452 if newpath in seen :
@@ -469,7 +478,9 @@ def realpath(filename, *, strict=False):
469478 rest .append (newpath )
470479 rest .append (None )
471480 # Push the unresolved symlink target parts onto the stack.
472- rest .extend (target .split (sep )[::- 1 ])
481+ target_parts = target .split (sep )[::- 1 ]
482+ rest .extend (target_parts )
483+ part_count += len (target_parts )
473484
474485 return path
475486
0 commit comments