aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/posixpath.py
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2023-01-27 00:28:27 +0000
committerGitHub <noreply@github.com>2023-01-27 00:28:27 +0000
commite5b08ddddf1099f04bf65e63017de840bd4b5980 (patch)
tree598fb062a99a7d159debc1d97398b04d2a88e7df /Lib/posixpath.py
parent37f15a5efab847b8aca47981ab596e9c36445bf7 (diff)
downloadcpython-e5b08ddddf1099f04bf65e63017de840bd4b5980.tar.gz
cpython-e5b08ddddf1099f04bf65e63017de840bd4b5980.zip
gh-101000: Add os.path.splitroot() (#101002)
Co-authored-by: Eryk Sun <eryksun@gmail.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Diffstat (limited to 'Lib/posixpath.py')
-rw-r--r--Lib/posixpath.py43
1 files changed, 32 insertions, 11 deletions
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
index 737f8a5c156..32b5d6e105d 100644
--- a/Lib/posixpath.py
+++ b/Lib/posixpath.py
@@ -28,7 +28,7 @@ import stat
import genericpath
from genericpath import *
-__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
+__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
"basename","dirname","commonprefix","getsize","getmtime",
"getatime","getctime","islink","exists","lexists","isdir","isfile",
"ismount", "expanduser","expandvars","normpath","abspath",
@@ -135,6 +135,35 @@ def splitdrive(p):
return p[:0], p
+def splitroot(p):
+ """Split a pathname into drive, root and tail. On Posix, drive is always
+ empty; the root may be empty, a single slash, or two slashes. The tail
+ contains anything after the root. For example:
+
+ splitroot('foo/bar') == ('', '', 'foo/bar')
+ splitroot('/foo/bar') == ('', '/', 'foo/bar')
+ splitroot('//foo/bar') == ('', '//', 'foo/bar')
+ splitroot('///foo/bar') == ('', '/', '//foo/bar')
+ """
+ p = os.fspath(p)
+ if isinstance(p, bytes):
+ sep = b'/'
+ empty = b''
+ else:
+ sep = '/'
+ empty = ''
+ if p[:1] != sep:
+ # Relative path, e.g.: 'foo'
+ return empty, empty, p
+ elif p[1:2] != sep or p[2:3] == sep:
+ # Absolute path, e.g.: '/foo', '///foo', '////foo', etc.
+ return empty, sep, p[1:]
+ else:
+ # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see
+ # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
+ return empty, p[:2], p[2:]
+
+
# Return the tail (basename) part of a path, same as split(path)[1].
def basename(p):
@@ -372,13 +401,7 @@ except ImportError:
dotdot = '..'
if path == empty:
return dot
- initial_slashes = path.startswith(sep)
- # POSIX allows one or two initial slashes, but treats three or more
- # as single slash.
- # (see https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13)
- if (initial_slashes and
- path.startswith(sep*2) and not path.startswith(sep*3)):
- initial_slashes = 2
+ _, initial_slashes, path = splitroot(path)
comps = path.split(sep)
new_comps = []
for comp in comps:
@@ -390,9 +413,7 @@ except ImportError:
elif new_comps:
new_comps.pop()
comps = new_comps
- path = sep.join(comps)
- if initial_slashes:
- path = sep*initial_slashes + path
+ path = initial_slashes + sep.join(comps)
return path or dot
else: