Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
gh-101000: Add os.path.splitroot()#101002
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
21c0ba9836b85dbc2d1f9ecdc40d6592b2778f42279726ca47a6613c26a8dba0c237d411ed3eb2c9eed88299e9627ffe379beff2abacdee14ebe5452927afeb0aa73e19777d632e212e37cded35a8dfce0e75a553663237053729d694f093e99e3cdf618a001c522c9df17269File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -488,6 +488,39 @@ the :mod:`glob` module.) | ||
| Accepts a :term:`path-like object`. | ||
| .. function:: splitroot(path) | ||
| Split the pathname *path* into a 3-item tuple ``(drive, root, tail)`` where | ||
| *drive* is a device name or mount point, *root* is a string of separators | ||
| after the drive, and *tail* is everything after the root. Any of these | ||
| items may be the empty string. In all cases, ``drive + root + tail`` will | ||
| be the same as *path*. | ||
| On POSIX systems, *drive* is always empty. The *root* may be empty (if *path* is | ||
| relative), a single forward slash (if *path* is absolute), or two forward slashes | ||
| (implementation-defined per `IEEE Std 1003.1-2017; 4.13 Pathname Resolution | ||
| <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>`_.) | ||
| For example:: | ||
| >>> splitroot('/home/sam') | ||
| ('', '/', 'home/sam') | ||
| >>> splitroot('//home/sam') | ||
| ('', '//', 'home/sam') | ||
| >>> splitroot('///home/sam') | ||
| ('', '/', '//home/sam') | ||
| On Windows, *drive* may be empty, a drive-letter name, a UNC share, or a device | ||
| name. The *root* may be empty, a forward slash, or a backward slash. For | ||
| example:: | ||
| >>> splitroot('C:/Users/Sam') | ||
| ('C:', '/', 'Users/Sam') | ||
| >>> splitroot('//Server/Share/Users/Sam') | ||
| ('//Server/Share', '/', 'Users/Sam') | ||
AlexWaygood marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| .. versionadded:: 3.12 | ||
barneygale marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| .. function:: splitext(path) | ||
| Split the pathname *path* into a pair ``(root, ext)`` such that ``root + ext == | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -24,7 +24,7 @@ | ||
| from genericpath import * | ||
| __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", | ||
| @@ -117,19 +117,21 @@ def join(path, *paths): | ||
| try: | ||
| if not paths: | ||
| path[:0] + sep #23780: Ensure compatible data type even if p is null. | ||
| result_drive,result_root,result_path =splitroot(path) | ||
| for p in map(os.fspath, paths): | ||
| p_drive,p_root,p_path =splitroot(p) | ||
| ifp_root: | ||
| # Second path is absolute | ||
| if p_drive or not result_drive: | ||
| result_drive = p_drive | ||
| result_root = p_root | ||
| result_path = p_path | ||
| continue | ||
| elif p_drive and p_drive != result_drive: | ||
| if p_drive.lower() != result_drive.lower(): | ||
| # Different drives => ignore the first path entirely | ||
| result_drive = p_drive | ||
| result_root = p_root | ||
| result_path = p_path | ||
| continue | ||
| # Same drive in different case | ||
| @@ -139,10 +141,10 @@ def join(path, *paths): | ||
| result_path = result_path + sep | ||
| result_path = result_path + p_path | ||
| ## add separator between UNC and non-absolute path | ||
| if (result_path and notresult_root and | ||
| result_drive and result_drive[-1:] != colon): | ||
| return result_drive + sep + result_path | ||
| return result_drive +result_root +result_path | ||
| except (TypeError, AttributeError, BytesWarning): | ||
| genericpath._check_arg_types('join', path, *paths) | ||
| raise | ||
| @@ -169,35 +171,61 @@ def splitdrive(p): | ||
| Paths cannot contain both a drive letter and a UNC path. | ||
| """ | ||
| drive, root, tail = splitroot(p) | ||
| return drive, root + tail | ||
| def splitroot(p): | ||
| """Split a pathname into drive, root and tail. The drive is defined | ||
| exactly as in splitdrive(). On Windows, the root may be a single path | ||
| separator or an empty string. The tail contains anything after the root. | ||
| For example: | ||
| splitroot('//server/share/') == ('//server/share', '/', '') | ||
| splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney') | ||
| splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham') | ||
| splitroot('Windows/notepad') == ('', '', 'Windows/notepad') | ||
| """ | ||
| p = os.fspath(p) | ||
| if isinstance(p, bytes): | ||
| sep = b'\\' | ||
| altsep = b'/' | ||
| colon = b':' | ||
| unc_prefix = b'\\\\?\\UNC\\' | ||
| empty = b'' | ||
| else: | ||
| sep = '\\' | ||
| altsep = '/' | ||
| colon = ':' | ||
| unc_prefix = '\\\\?\\UNC\\' | ||
| empty = '' | ||
| normp = p.replace(altsep, sep) | ||
| if normp[:1] == sep: | ||
| if normp[1:2] == sep: | ||
| # UNC drives, e.g. \\server\share or \\?\UNC\server\share | ||
| # Device drives, e.g. \\.\device or \\?\device | ||
| start = 8 if normp[:8].upper() == unc_prefix else 2 | ||
| index = normp.find(sep, start) | ||
| if index == -1: | ||
| return p,empty, empty | ||
| index2 = normp.find(sep, index + 1) | ||
| if index2 == -1: | ||
| return p, empty, empty | ||
| return p[:index2], p[index2:index2 + 1], p[index2 + 1:] | ||
| else: | ||
| # Relative path with root, e.g. \Windows | ||
| return empty, p[:1], p[1:] | ||
| elif normp[1:2] == colon: | ||
| if normp[2:3] == sep: | ||
| # Absolute drive-letter path, e.g. X:\Windows | ||
| return p[:2], p[2:3], p[3:] | ||
AlexWaygood marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| else: | ||
| # Relative path with drive, e.g. X:Windows | ||
| return p[:2], empty, p[2:] | ||
| else: | ||
| # Relative path, e.g. Windows | ||
| return empty, empty, p | ||
| # Split a path in head (everything up to the last '/') and tail (the | ||
| @@ -212,15 +240,13 @@ def split(p): | ||
| Either part may be empty.""" | ||
| p = os.fspath(p) | ||
| seps = _get_bothseps(p) | ||
| d,r,p =splitroot(p) | ||
| # set i to index beyond p's last slash | ||
| i = len(p) | ||
| while i and p[i-1] not in seps: | ||
| i -= 1 | ||
| head, tail = p[:i], p[i:] # now tail has no slashes | ||
| return d + r + head.rstrip(seps), tail | ||
barneygale marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| # Split a path in root and extension. | ||
| @@ -311,10 +337,10 @@ def ismount(path): | ||
| path = os.fspath(path) | ||
| seps = _get_bothseps(path) | ||
| path = abspath(path) | ||
| drive,root, rest =splitroot(path) | ||
| ifdrive anddrive[0] in seps: | ||
| return not rest | ||
| ifroot andnot rest: | ||
| return True | ||
| if _getvolumepathname: | ||
| @@ -525,13 +551,8 @@ def normpath(path): | ||
| curdir = '.' | ||
| pardir = '..' | ||
| path = path.replace(altsep, sep) | ||
| drive, root, path = splitroot(path) | ||
| prefix = drive + root | ||
| comps = path.split(sep) | ||
| i = 0 | ||
| while i < len(comps): | ||
| @@ -541,7 +562,7 @@ def normpath(path): | ||
| if i > 0 and comps[i-1] != pardir: | ||
| del comps[i-1:i+1] | ||
| i -= 1 | ||
| elif i == 0 androot: | ||
| del comps[i] | ||
| else: | ||
| i += 1 | ||
| @@ -765,8 +786,8 @@ def relpath(path, start=None): | ||
| try: | ||
| start_abs = abspath(normpath(start)) | ||
| path_abs = abspath(normpath(path)) | ||
| start_drive,_,start_rest =splitroot(start_abs) | ||
| path_drive,_,path_rest =splitroot(path_abs) | ||
barneygale marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| if normcase(start_drive) != normcase(path_drive): | ||
| raise ValueError("path is on mount %r, start on mount %r" % ( | ||
| path_drive, start_drive)) | ||
| @@ -816,21 +837,19 @@ def commonpath(paths): | ||
| curdir = '.' | ||
| try: | ||
| drivesplits = [splitroot(p.replace(altsep, sep).lower()) for p in paths] | ||
| split_paths = [p.split(sep) for d,r,p in drivesplits] | ||
| if len({r for d, r, p in drivesplits}) != 1: | ||
| raise ValueError("Can't mix absolute and relative paths") | ||
| # Check that all drive letters or UNC paths match. The check is made only | ||
| # now otherwise type errors for mixing strings and bytes would not be | ||
| # caught. | ||
| if len({d for d,r,p in drivesplits}) != 1: | ||
| raise ValueError("Paths don't have the same drive") | ||
| drive,root,path =splitroot(paths[0].replace(altsep, sep)) | ||
| common = path.split(sep) | ||
| common = [c for c in common if c and c != curdir] | ||
| @@ -844,8 +863,7 @@ def commonpath(paths): | ||
| else: | ||
| common = common[:len(s1)] | ||
| return drive + root + sep.join(common) | ||
| except (TypeError, AttributeError): | ||
| genericpath._check_arg_types('commonpath', *paths) | ||
| raise | ||
Uh oh!
There was an error while loading.Please reload this page.