diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index b1204e88044a3f..340596d7f873d5 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -956,9 +956,15 @@ def _split_stack(self): uppermost parent of the path (equivalent to path.parents[-1]), and *parts* is a reversed list of parts following the anchor. """ - if not self._tail: - return self, [] - return self._from_parsed_parts(self.drive, self.root, []), self._tail[::-1] + split = self.pathmod.split + path = str(self) + parent, name = split(path) + names = [] + while path != parent: + names.append(name) + path = parent + parent, name = split(path) + return path, names def resolve(self, strict=False): """ @@ -967,11 +973,15 @@ def resolve(self, strict=False): """ if self._resolving: return self - path, parts = self._split_stack() + path_root, parts = self._split_stack() + path = self.with_segments(path_root) try: path = path.absolute() except UnsupportedOperation: - pass + path_tail = [] + else: + path_root, path_tail = path._split_stack() + path_tail.reverse() # If the user has *not* overridden the `readlink()` method, then symlinks are unsupported # and (in non-strict mode) we can improve performance by not calling `stat()`. @@ -979,31 +989,37 @@ def resolve(self, strict=False): link_count = 0 while parts: part = parts.pop() + if not part or part == '.': + continue if part == '..': - if not path._tail: - if path.root: + if not path_tail: + if path_root: # Delete '..' segment immediately following root continue - elif path._tail[-1] != '..': + elif path_tail[-1] != '..': # Delete '..' segment and its predecessor - path = path.parent + path_tail.pop() continue - next_path = path._make_child_relpath(part) + path_tail.append(part) if querying and part != '..': - next_path._resolving = True + path = self.with_segments(path_root + self.pathmod.sep.join(path_tail)) + path._resolving = True try: - st = next_path.stat(follow_symlinks=False) + st = path.stat(follow_symlinks=False) if S_ISLNK(st.st_mode): # Like Linux and macOS, raise OSError(errno.ELOOP) if too many symlinks are # encountered during resolution. link_count += 1 if link_count >= _MAX_SYMLINKS: raise OSError(ELOOP, "Too many symbolic links in path", str(self)) - target, target_parts = next_path.readlink()._split_stack() + target_root, target_parts = path.readlink()._split_stack() # If the symlink target is absolute (like '/etc/hosts'), set the current # path to its uppermost parent (like '/'). - if target.root: - path = target + if target_root: + path_root = target_root + path_tail.clear() + else: + path_tail.pop() # Add the symlink target's reversed tail parts (like ['hosts', 'etc']) to # the stack of unresolved path parts. parts.extend(target_parts) @@ -1015,9 +1031,7 @@ def resolve(self, strict=False): raise else: querying = False - next_path._resolving = False - path = next_path - return path + return self.with_segments(path_root + self.pathmod.sep.join(path_tail)) def symlink_to(self, target, target_is_directory=False): """