diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 02eb5c25981e31..f753e81f030e4e 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -383,6 +383,27 @@ def with_segments(self, *pathsegments): are created from methods like `iterdir()`. """ return type(self)(*pathsegments) + + def erase_parents(self): + r""" + Remove path components referring to parent directories ('..'), + without any attempt to verify the existence or structure of any + path components. + + If this is a relative path, there may be leftover '..' segments + after erasing parent segments. For example: + + - Path('spam/../../eggs').erase_parents() == Path('../eggs') + + If this is an absolute path, and it reaches the root filesystem + (or the root of a drive or UNC share for Windows paths), + further '..' components will be ignored. For example: + + - Path('/spam/../../../eggs').erase_parents() == Path('/eggs') + - PureWindowsPath(r'c:\spam\..\..\eggs').erase_parents() == PureWindowsPath(r'c:\eggs') + - PureWindowsPath(r'\\server\share\foo\..\..\eggs').erase_parents() == PureWindowsPath(r'\\server\share\eggs') + """ + return type(self)(self._flavour.normpath(self)) @classmethod def _parse_path(cls, path): @@ -479,7 +500,7 @@ def as_uri(self): # It's a posix path => 'file:///etc/hosts' prefix = 'file://' path = str(self) - return prefix + urlquote_from_bytes(os.fsencode(path)) + return prefix + urlquote_from_bytes(os.fsencode(path)) @property def _str_normcase(self): diff --git a/Misc/NEWS.d/next/Library/2025-06-11-19-35-18.gh-issue-124825.ta9HSE.rst b/Misc/NEWS.d/next/Library/2025-06-11-19-35-18.gh-issue-124825.ta9HSE.rst new file mode 100644 index 00000000000000..e0797a435483f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-11-19-35-18.gh-issue-124825.ta9HSE.rst @@ -0,0 +1 @@ +Added :func:`pathlib.PurePath.erase_parents`, which will remove path components referring to parent directories ('..') without any attempt to verify the existence or structure of any path components.