Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 92 additions & 79 deletions Lib/ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,52 +168,59 @@ def splitdrive(p, /):
return drive, root + tail


def splitroot(p, /):
"""Split a pathname into drive, root and tail.

The tail contains anything after the root."""
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:]
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


# Kept under a private name so that nt._path_splitroot_ex() can delegate to it
# for bytes paths that cannot be decoded with the filesystem encoding.
_splitroot_fallback = splitroot

try:
from nt import _path_splitroot_ex as splitroot
except ImportError:
def splitroot(p, /):
"""Split a pathname into drive, root and tail.

The tail contains anything after the root."""
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:]
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
pass


# Split a path in head (everything up to the last '/') and tail (the
Expand Down Expand Up @@ -470,45 +477,51 @@ def repl(m):
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
# Previously, this function also truncated pathnames to 8+3 format,
# but as this module is called "ntpath", that's obviously wrong!
try:
from nt import _path_normpath as normpath

except ImportError:
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
sep = b'\\'
altsep = b'/'
curdir = b'.'
pardir = b'..'
else:
sep = '\\'
altsep = '/'
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):
if not comps[i] or comps[i] == curdir:
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
sep = b'\\'
altsep = b'/'
curdir = b'.'
pardir = b'..'
else:
sep = '\\'
altsep = '/'
curdir = '.'
pardir = '..'
path = path.replace(altsep, sep)
drive, root, path = _splitroot_fallback(path)
prefix = drive + root
comps = path.split(sep)
i = 0
while i < len(comps):
if not comps[i] or comps[i] == curdir:
del comps[i]
elif comps[i] == pardir:
if i > 0 and comps[i-1] != pardir:
del comps[i-1:i+1]
i -= 1
elif i == 0 and root:
del comps[i]
elif comps[i] == pardir:
if i > 0 and comps[i-1] != pardir:
del comps[i-1:i+1]
i -= 1
elif i == 0 and root:
del comps[i]
else:
i += 1
else:
i += 1
# If the path is now empty, substitute '.'
if not prefix and not comps:
comps.append(curdir)
return prefix + sep.join(comps)
else:
i += 1
# If the path is now empty, substitute '.'
if not prefix and not comps:
comps.append(curdir)
return prefix + sep.join(comps)


# Kept under a private name so that nt._path_normpath() can delegate to it for
# bytes paths that cannot be decoded with the filesystem encoding.
_normpath_fallback = normpath

try:
from nt import _path_normpath as normpath
except ImportError:
pass


# Return an absolute path.
Expand Down
111 changes: 62 additions & 49 deletions Lib/posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,30 +136,37 @@ def splitdrive(p, /):
return p[:0], p


def splitroot(p, /):
"""Split a pathname into drive, root and tail.

The tail contains anything after the root."""
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:]


# Kept under a private name so that posix._path_splitroot_ex() can delegate to
# it for bytes paths that cannot be decoded with the filesystem encoding.
_splitroot_fallback = splitroot

try:
from posix import _path_splitroot_ex as splitroot
except ImportError:
def splitroot(p, /):
"""Split a pathname into drive, root and tail.

The tail contains anything after the root."""
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:]
pass


# Return the tail (basename) part of a path, same as split(path)[1].
Expand Down Expand Up @@ -338,37 +345,43 @@ def repl(m):
# It should be understood that this may change the meaning of the path
# if it contains symbolic links!

def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
sep = b'/'
dot = b'.'
dotdot = b'..'
else:
sep = '/'
dot = '.'
dotdot = '..'
if not path:
return dot
_, initial_slashes, path = _splitroot_fallback(path)
comps = path.split(sep)
new_comps = []
for comp in comps:
if not comp or comp == dot:
continue
if (comp != dotdot or (not initial_slashes and not new_comps) or
(new_comps and new_comps[-1] == dotdot)):
new_comps.append(comp)
elif new_comps:
new_comps.pop()
comps = new_comps
path = initial_slashes + sep.join(comps)
return path or dot


# Kept under a private name so that posix._path_normpath() can delegate to it
# for bytes paths that cannot be decoded with the filesystem encoding.
_normpath_fallback = normpath

try:
from posix import _path_normpath as normpath

except ImportError:
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
sep = b'/'
dot = b'.'
dotdot = b'..'
else:
sep = '/'
dot = '.'
dotdot = '..'
if not path:
return dot
_, initial_slashes, path = splitroot(path)
comps = path.split(sep)
new_comps = []
for comp in comps:
if not comp or comp == dot:
continue
if (comp != dotdot or (not initial_slashes and not new_comps) or
(new_comps and new_comps[-1] == dotdot)):
new_comps.append(comp)
elif new_comps:
new_comps.pop()
comps = new_comps
path = initial_slashes + sep.join(comps)
return path or dot
pass


def abspath(path):
Expand Down
Loading
Loading