Update {nt,posix}path.py from 3.13.5 (#6070)

* Update `{nt,posix}path.py` from 3.13.5

* Mark failing tests
This commit is contained in:
Shahar Naveh
2025-08-05 16:18:10 +02:00
committed by GitHub
parent f5a77a1f68
commit 91979a3d0e
4 changed files with 1343 additions and 538 deletions

367
Lib/ntpath.py vendored
View File

@@ -19,18 +19,17 @@ devnull = 'nul'
import os
import sys
import stat
import genericpath
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",
"curdir","pardir","sep","pathsep","defpath","altsep",
"ismount","isreserved","expanduser","expandvars","normpath",
"abspath","curdir","pardir","sep","pathsep","defpath","altsep",
"extsep","devnull","realpath","supports_unicode_filenames","relpath",
"samefile", "sameopenfile", "samestat", "commonpath", "isjunction"]
"samefile", "sameopenfile", "samestat", "commonpath", "isjunction",
"isdevdrive", "ALLOW_MISSING"]
def _get_bothseps(path):
if isinstance(path, bytes):
@@ -78,12 +77,6 @@ except ImportError:
return s.replace('/', '\\').lower()
# Return whether a path is absolute.
# Trivial in Posix, harder on Windows.
# For Windows it is absolute if it starts with a slash or backslash (current
# volume), or if a pathname after the volume-letter-and-colon or UNC-resource
# starts with a slash or backslash.
def isabs(s):
"""Test whether a path is absolute"""
s = os.fspath(s)
@@ -91,16 +84,15 @@ def isabs(s):
sep = b'\\'
altsep = b'/'
colon_sep = b':\\'
double_sep = b'\\\\'
else:
sep = '\\'
altsep = '/'
colon_sep = ':\\'
double_sep = '\\\\'
s = s[:3].replace(altsep, sep)
# Absolute: UNC, device, and paths with a drive and root.
# LEGACY BUG: isabs("/x") should be false since the path has no drive.
if s.startswith(sep) or s.startswith(colon_sep, 1):
return True
return False
return s.startswith(colon_sep, 1) or s.startswith(double_sep)
# Join two (or more) paths.
@@ -109,16 +101,14 @@ def join(path, *paths):
if isinstance(path, bytes):
sep = b'\\'
seps = b'\\/'
colon = b':'
colon_seps = b':\\/'
else:
sep = '\\'
seps = '\\/'
colon = ':'
colon_seps = ':\\/'
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):
for p in paths:
p_drive, p_root, p_path = splitroot(p)
if p_root:
# Second path is absolute
@@ -142,7 +132,7 @@ def join(path, *paths):
result_path = result_path + p_path
## add separator between UNC and non-absolute path
if (result_path and not result_root and
result_drive and result_drive[-1:] not in colon + seps):
result_drive and result_drive[-1] not in colon_seps):
return result_drive + sep + result_path
return result_drive + result_root + result_path
except (TypeError, AttributeError, BytesWarning):
@@ -176,56 +166,52 @@ def splitdrive(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:
try:
from nt import _path_splitroot_ex as splitroot
except ImportError:
def splitroot(p):
"""Split a pathname into drive, root and tail.
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:]
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:
# 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:]
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 with drive, e.g. X:Windows
return p[:2], empty, p[2:]
else:
# Relative path, e.g. Windows
return empty, empty, p
# Relative path, e.g. Windows
return empty, empty, p
# Split a path in head (everything up to the last '/') and tail (the
@@ -277,33 +263,6 @@ def dirname(p):
return split(p)[0]
# Is a path a junction?
if hasattr(os.stat_result, 'st_reparse_tag'):
def isjunction(path):
"""Test whether a path is a junction"""
try:
st = os.lstat(path)
except (OSError, ValueError, AttributeError):
return False
return bool(st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)
else:
def isjunction(path):
"""Test whether a path is a junction"""
os.fspath(path)
return False
# Being true for dangling symbolic links is also useful.
def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
st = os.lstat(path)
except (OSError, ValueError):
return False
return True
# Is a path a mount point?
# Any drive letter root (eg c:\)
# Any share UNC (eg \\server\share)
@@ -338,6 +297,40 @@ def ismount(path):
return False
_reserved_chars = frozenset(
{chr(i) for i in range(32)} |
{'"', '*', ':', '<', '>', '?', '|', '/', '\\'}
)
_reserved_names = frozenset(
{'CON', 'PRN', 'AUX', 'NUL', 'CONIN$', 'CONOUT$'} |
{f'COM{c}' for c in '123456789\xb9\xb2\xb3'} |
{f'LPT{c}' for c in '123456789\xb9\xb2\xb3'}
)
def isreserved(path):
"""Return true if the pathname is reserved by the system."""
# Refer to "Naming Files, Paths, and Namespaces":
# https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
path = os.fsdecode(splitroot(path)[2]).replace(altsep, sep)
return any(_isreservedname(name) for name in reversed(path.split(sep)))
def _isreservedname(name):
"""Return true if the filename is reserved by the system."""
# Trailing dots and spaces are reserved.
if name[-1:] in ('.', ' '):
return name not in ('.', '..')
# Wildcards, separators, colon, and pipe (*?"<>/\:|) are reserved.
# ASCII control characters (0-31) are reserved.
# Colon is reserved for file streams (e.g. "name:stream[:type]").
if _reserved_chars.intersection(name):
return True
# DOS device names are reserved (e.g. "nul" or "nul .txt"). The rules
# are complex and vary across Windows versions. On the side of
# caution, return True for names that may not be reserved.
return name.partition('.')[0].rstrip(' ').upper() in _reserved_names
# Expand paths beginning with '~' or '~user'.
# '~' means $HOME; '~user' means that user's home directory.
# If the path doesn't begin with '~', or if the user or $HOME is unknown,
@@ -353,24 +346,23 @@ def expanduser(path):
If user or $HOME is unknown, do nothing."""
path = os.fspath(path)
if isinstance(path, bytes):
seps = b'\\/'
tilde = b'~'
else:
seps = '\\/'
tilde = '~'
if not path.startswith(tilde):
return path
i, n = 1, len(path)
while i < n and path[i] not in _get_bothseps(path):
while i < n and path[i] not in seps:
i += 1
if 'USERPROFILE' in os.environ:
userhome = os.environ['USERPROFILE']
elif not 'HOMEPATH' in os.environ:
elif 'HOMEPATH' not in os.environ:
return path
else:
try:
drive = os.environ['HOMEDRIVE']
except KeyError:
drive = ''
drive = os.environ.get('HOMEDRIVE', '')
userhome = join(drive, os.environ['HOMEPATH'])
if i != 1: #~user
@@ -521,7 +513,7 @@ def expandvars(path):
# 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
from nt import _path_normpath as normpath
except ImportError:
def normpath(path):
@@ -560,37 +552,22 @@ except ImportError:
comps.append(curdir)
return prefix + sep.join(comps)
else:
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
return os.fsencode(_path_normpath(os.fsdecode(path))) or b"."
return _path_normpath(path) or "."
def _abspath_fallback(path):
"""Return the absolute version of a path as a fallback function in case
`nt._getfullpathname` is not available or raises OSError. See bpo-31047 for
more.
"""
path = os.fspath(path)
if not isabs(path):
if isinstance(path, bytes):
cwd = os.getcwdb()
else:
cwd = os.getcwd()
path = join(cwd, path)
return normpath(path)
# Return an absolute path.
try:
from nt import _getfullpathname
except ImportError: # not running on Windows - mock up something sensible
abspath = _abspath_fallback
def abspath(path):
"""Return the absolute version of a path."""
path = os.fspath(path)
if not isabs(path):
if isinstance(path, bytes):
cwd = os.getcwdb()
else:
cwd = os.getcwd()
path = join(cwd, path)
return normpath(path)
else: # use native Windows method on Windows
def abspath(path):
@@ -598,15 +575,36 @@ else: # use native Windows method on Windows
try:
return _getfullpathname(normpath(path))
except (OSError, ValueError):
return _abspath_fallback(path)
# See gh-75230, handle outside for cleaner traceback
pass
path = os.fspath(path)
if not isabs(path):
if isinstance(path, bytes):
sep = b'\\'
getcwd = os.getcwdb
else:
sep = '\\'
getcwd = os.getcwd
drive, root, path = splitroot(path)
# Either drive or root can be nonempty, but not both.
if drive or root:
try:
path = join(_getfullpathname(drive + root), path)
except (OSError, ValueError):
# Drive "\0:" cannot exist; use the root directory.
path = drive + sep + path
else:
path = join(getcwd(), path)
return normpath(path)
try:
from nt import _getfinalpathname, readlink as _nt_readlink
from nt import _findfirstfile, _getfinalpathname, readlink as _nt_readlink
except ImportError:
# realpath is a no-op on systems without _getfinalpathname support.
realpath = abspath
def realpath(path, *, strict=False):
return abspath(path)
else:
def _readlink_deep(path):
def _readlink_deep(path, ignored_error=OSError):
# These error codes indicate that we should stop reading links and
# return the path we currently have.
# 1: ERROR_INVALID_FUNCTION
@@ -639,7 +637,7 @@ else:
path = old_path
break
path = normpath(join(dirname(old_path), path))
except OSError as ex:
except ignored_error as ex:
if ex.winerror in allowed_winerror:
break
raise
@@ -648,7 +646,7 @@ else:
break
return path
def _getfinalpathname_nonstrict(path):
def _getfinalpathname_nonstrict(path, ignored_error=OSError):
# These error codes indicate that we should stop resolving the path
# and return the value we currently have.
# 1: ERROR_INVALID_FUNCTION
@@ -664,9 +662,10 @@ else:
# 87: ERROR_INVALID_PARAMETER
# 123: ERROR_INVALID_NAME
# 161: ERROR_BAD_PATHNAME
# 1005: ERROR_UNRECOGNIZED_VOLUME
# 1920: ERROR_CANT_ACCESS_FILE
# 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1920, 1921
allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1005, 1920, 1921
# Non-strict algorithm is to find as much of the target directory
# as we can and join the rest.
@@ -675,23 +674,29 @@ else:
try:
path = _getfinalpathname(path)
return join(path, tail) if tail else path
except OSError as ex:
except ignored_error as ex:
if ex.winerror not in allowed_winerror:
raise
try:
# The OS could not resolve this path fully, so we attempt
# to follow the link ourselves. If we succeed, join the tail
# and return.
new_path = _readlink_deep(path)
new_path = _readlink_deep(path,
ignored_error=ignored_error)
if new_path != path:
return join(new_path, tail) if tail else new_path
except OSError:
except ignored_error:
# If we fail to readlink(), let's keep traversing
pass
path, name = split(path)
# TODO (bpo-38186): Request the real file name from the directory
# entry using FindFirstFileW. For now, we will return the path
# as best we have it
# If we get these errors, try to get the real name of the file without accessing it.
if ex.winerror in (1, 5, 32, 50, 87, 1920, 1921):
try:
name = _findfirstfile(path)
path, _ = split(path)
except ignored_error:
path, name = split(path)
else:
path, name = split(path)
if path and not name:
return path + tail
tail = join(name, tail) if tail else name
@@ -705,7 +710,8 @@ else:
new_unc_prefix = b'\\\\'
cwd = os.getcwdb()
# bpo-38081: Special case for realpath(b'nul')
if normcase(path) == normcase(os.fsencode(devnull)):
devnull = b'nul'
if normcase(path) == devnull:
return b'\\\\.\\NUL'
else:
prefix = '\\\\?\\'
@@ -713,9 +719,19 @@ else:
new_unc_prefix = '\\\\'
cwd = os.getcwd()
# bpo-38081: Special case for realpath('nul')
if normcase(path) == normcase(devnull):
devnull = 'nul'
if normcase(path) == devnull:
return '\\\\.\\NUL'
had_prefix = path.startswith(prefix)
if strict is ALLOW_MISSING:
ignored_error = FileNotFoundError
strict = True
elif strict:
ignored_error = ()
else:
ignored_error = OSError
if not had_prefix and not isabs(path):
path = join(cwd, path)
try:
@@ -723,17 +739,16 @@ else:
initial_winerror = 0
except ValueError as ex:
# gh-106242: Raised for embedded null characters
# In strict mode, we convert into an OSError.
# In strict modes, we convert into an OSError.
# Non-strict mode returns the path as-is, since we've already
# made it absolute.
if strict:
raise OSError(str(ex)) from None
path = normpath(path)
except OSError as ex:
if strict:
raise
except ignored_error as ex:
initial_winerror = ex.winerror
path = _getfinalpathname_nonstrict(path)
path = _getfinalpathname_nonstrict(path,
ignored_error=ignored_error)
# The path returned by _getfinalpathname will always start with \\?\ -
# strip off that prefix unless it was already provided on the original
# path.
@@ -766,6 +781,9 @@ supports_unicode_filenames = True
def relpath(path, start=None):
"""Return a relative version of a path"""
path = os.fspath(path)
if not path:
raise ValueError("no path specified")
if isinstance(path, bytes):
sep = b'\\'
curdir = b'.'
@@ -777,22 +795,20 @@ def relpath(path, start=None):
if start is None:
start = curdir
else:
start = os.fspath(start)
if not path:
raise ValueError("no path specified")
start = os.fspath(start)
try:
start_abs = abspath(normpath(start))
path_abs = abspath(normpath(path))
start_abs = abspath(start)
path_abs = abspath(path)
start_drive, _, start_rest = splitroot(start_abs)
path_drive, _, path_rest = splitroot(path_abs)
if normcase(start_drive) != normcase(path_drive):
raise ValueError("path is on mount %r, start on mount %r" % (
path_drive, start_drive))
start_list = [x for x in start_rest.split(sep) if x]
path_list = [x for x in path_rest.split(sep) if x]
start_list = start_rest.split(sep) if start_rest else []
path_list = path_rest.split(sep) if path_rest else []
# Work out how much of the filepath is shared by start and path.
i = 0
for e1, e2 in zip(start_list, path_list):
@@ -803,29 +819,28 @@ def relpath(path, start=None):
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
if not rel_list:
return curdir
return join(*rel_list)
return sep.join(rel_list)
except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
genericpath._check_arg_types('relpath', path, start)
raise
# Return the longest common sub-path of the sequence of paths given as input.
# Return the longest common sub-path of the iterable of paths given as input.
# The function is case-insensitive and 'separator-insensitive', i.e. if the
# only difference between two paths is the use of '\' versus '/' as separator,
# they are deemed to be equal.
#
# However, the returned path will have the standard '\' separator (even if the
# given paths had the alternative '/' separator) and will have the case of the
# first path given in the sequence. Additionally, any trailing separator is
# first path given in the iterable. Additionally, any trailing separator is
# stripped from the returned path.
def commonpath(paths):
"""Given a sequence of path names, returns the longest common sub-path."""
if not paths:
raise ValueError('commonpath() arg is an empty sequence')
"""Given an iterable of path names, returns the longest common sub-path."""
paths = tuple(map(os.fspath, paths))
if not paths:
raise ValueError('commonpath() arg is an empty iterable')
if isinstance(paths[0], bytes):
sep = b'\\'
altsep = b'/'
@@ -839,9 +854,6 @@ def commonpath(paths):
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.
@@ -849,6 +861,12 @@ def commonpath(paths):
raise ValueError("Paths don't have the same drive")
drive, root, path = splitroot(paths[0].replace(altsep, sep))
if len({r for d, r, p in drivesplits}) != 1:
if drive:
raise ValueError("Can't mix absolute and relative paths")
else:
raise ValueError("Can't mix rooted and not-rooted paths")
common = path.split(sep)
common = [c for c in common if c and c != curdir]
@@ -869,13 +887,15 @@ def commonpath(paths):
try:
# The isdir(), isfile(), islink() and exists() implementations in
# genericpath use os.stat(). This is overkill on Windows. Use simpler
# The isdir(), isfile(), islink(), exists() and lexists() implementations
# in genericpath use os.stat(). This is overkill on Windows. Use simpler
# builtin functions if they are available.
from nt import _path_isdir as isdir
from nt import _path_isfile as isfile
from nt import _path_islink as islink
from nt import _path_isjunction as isjunction
from nt import _path_exists as exists
from nt import _path_lexists as lexists
except ImportError:
# Use genericpath.* as imported above
pass
@@ -883,15 +903,12 @@ except ImportError:
try:
from nt import _path_isdevdrive
except ImportError:
def isdevdrive(path):
"""Determines whether the specified path is on a Windows Dev Drive."""
# Never a Dev Drive
return False
else:
def isdevdrive(path):
"""Determines whether the specified path is on a Windows Dev Drive."""
try:
return _path_isdevdrive(abspath(path))
except OSError:
return False
except ImportError:
# Use genericpath.isdevdrive as imported above
pass

286
Lib/posixpath.py vendored
View File

@@ -22,6 +22,7 @@ defpath = '/bin:/usr/bin'
altsep = None
devnull = '/dev/null'
import errno
import os
import sys
import stat
@@ -35,7 +36,7 @@ __all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext"
"samefile","sameopenfile","samestat",
"curdir","pardir","sep","pathsep","defpath","altsep","extsep",
"devnull","realpath","supports_unicode_filenames","relpath",
"commonpath", "isjunction"]
"commonpath", "isjunction","isdevdrive","ALLOW_MISSING"]
def _get_sep(path):
@@ -77,12 +78,11 @@ def join(a, *p):
sep = _get_sep(a)
path = a
try:
if not p:
path[:0] + sep #23780: Ensure compatible data type even if p is null.
for b in map(os.fspath, p):
if b.startswith(sep):
for b in p:
b = os.fspath(b)
if b.startswith(sep) or not path:
path = b
elif not path or path.endswith(sep):
elif path.endswith(sep):
path += b
else:
path += sep + b
@@ -135,33 +135,30 @@ 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:
try:
from posix import _path_splitroot_ex as splitroot
except ImportError:
def splitroot(p):
"""Split a pathname into drive, root and tail.
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:]
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:]
# Return the tail (basename) part of a path, same as split(path)[1].
@@ -187,26 +184,6 @@ def dirname(p):
return head
# Is a path a junction?
def isjunction(path):
"""Test whether a path is a junction
Junctions are not a part of posix semantics"""
os.fspath(path)
return False
# Being true for dangling symbolic links is also useful.
def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
os.lstat(path)
except (OSError, ValueError):
return False
return True
# Is a path a mount point?
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
@@ -227,21 +204,17 @@ def ismount(path):
parent = join(path, b'..')
else:
parent = join(path, '..')
parent = realpath(parent)
try:
s2 = os.lstat(parent)
except (OSError, ValueError):
return False
except OSError:
parent = realpath(parent)
try:
s2 = os.lstat(parent)
except OSError:
return False
dev1 = s1.st_dev
dev2 = s2.st_dev
if dev1 != dev2:
return True # path/.. on a different device as path
ino1 = s1.st_ino
ino2 = s2.st_ino
if ino1 == ino2:
return True # path/.. is the same i-node as path
return False
# path/.. on a different device as path or the same i-node as path
return s1.st_dev != s2.st_dev or s1.st_ino == s2.st_ino
# Expand paths beginning with '~' or '~user'.
@@ -290,7 +263,7 @@ def expanduser(path):
return path
name = path[1:i]
if isinstance(name, bytes):
name = str(name, 'ASCII')
name = os.fsdecode(name)
try:
pwent = pwd.getpwnam(name)
except KeyError:
@@ -303,11 +276,8 @@ def expanduser(path):
return path
if isinstance(path, bytes):
userhome = os.fsencode(userhome)
root = b'/'
else:
root = '/'
userhome = userhome.rstrip(root)
return (userhome + path[i:]) or root
userhome = userhome.rstrip(sep)
return (userhome + path[i:]) or sep
# Expand paths containing shell variable substitutions.
@@ -371,7 +341,7 @@ def expandvars(path):
# if it contains symbolic links!
try:
from posix import _path_normpath
from posix import _path_normpath as normpath
except ImportError:
def normpath(path):
@@ -379,21 +349,19 @@ except ImportError:
path = os.fspath(path)
if isinstance(path, bytes):
sep = b'/'
empty = b''
dot = b'.'
dotdot = b'..'
else:
sep = '/'
empty = ''
dot = '.'
dotdot = '..'
if path == empty:
if not path:
return dot
_, initial_slashes, path = splitroot(path)
comps = path.split(sep)
new_comps = []
for comp in comps:
if comp in (empty, dot):
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)):
@@ -404,24 +372,16 @@ except ImportError:
path = initial_slashes + sep.join(comps)
return path or dot
else:
def normpath(path):
"""Normalize path, eliminating double slashes, etc."""
path = os.fspath(path)
if isinstance(path, bytes):
return os.fsencode(_path_normpath(os.fsdecode(path))) or b"."
return _path_normpath(path) or "."
def abspath(path):
"""Return an absolute path."""
path = os.fspath(path)
if not isabs(path):
if isinstance(path, bytes):
cwd = os.getcwdb()
else:
cwd = os.getcwd()
path = join(cwd, path)
if isinstance(path, bytes):
if not path.startswith(b'/'):
path = join(os.getcwdb(), path)
else:
if not path.startswith('/'):
path = join(os.getcwd(), path)
return normpath(path)
@@ -432,72 +392,109 @@ def realpath(filename, *, strict=False):
"""Return the canonical path of the specified filename, eliminating any
symbolic links encountered in the path."""
filename = os.fspath(filename)
path, ok = _joinrealpath(filename[:0], filename, strict, {})
return abspath(path)
# Join two paths, normalizing and eliminating any symbolic links
# encountered in the second path.
def _joinrealpath(path, rest, strict, seen):
if isinstance(path, bytes):
if isinstance(filename, bytes):
sep = b'/'
curdir = b'.'
pardir = b'..'
getcwd = os.getcwdb
else:
sep = '/'
curdir = '.'
pardir = '..'
getcwd = os.getcwd
if strict is ALLOW_MISSING:
ignored_error = FileNotFoundError
strict = True
elif strict:
ignored_error = ()
else:
ignored_error = OSError
if isabs(rest):
rest = rest[1:]
path = sep
maxlinks = None
while rest:
name, _, rest = rest.partition(sep)
# The stack of unresolved path parts. When popped, a special value of None
# indicates that a symlink target has been resolved, and that the original
# symlink path can be retrieved by popping again. The [::-1] slice is a
# very fast way of spelling list(reversed(...)).
rest = filename.split(sep)[::-1]
# Number of unprocessed parts in 'rest'. This can differ from len(rest)
# later, because 'rest' might contain markers for unresolved symlinks.
part_count = len(rest)
# The resolved path, which is absolute throughout this function.
# Note: getcwd() returns a normalized and symlink-free path.
path = sep if filename.startswith(sep) else getcwd()
# Mapping from symlink paths to *fully resolved* symlink targets. If a
# symlink is encountered but not yet resolved, the value is None. This is
# used both to detect symlink loops and to speed up repeated traversals of
# the same links.
seen = {}
while part_count:
name = rest.pop()
if name is None:
# resolved symlink target
seen[rest.pop()] = path
continue
part_count -= 1
if not name or name == curdir:
# current dir
continue
if name == pardir:
# parent dir
if path:
path, name = split(path)
if name == pardir:
path = join(path, pardir, pardir)
else:
path = pardir
path = path[:path.rindex(sep)] or sep
continue
newpath = join(path, name)
try:
st = os.lstat(newpath)
except OSError:
if strict:
raise
is_link = False
if path == sep:
newpath = path + name
else:
is_link = stat.S_ISLNK(st.st_mode)
if not is_link:
path = newpath
continue
# Resolve the symbolic link
if newpath in seen:
# Already seen this path
path = seen[newpath]
if path is not None:
# use cached value
newpath = path + sep + name
try:
st_mode = os.lstat(newpath).st_mode
if not stat.S_ISLNK(st_mode):
if strict and part_count and not stat.S_ISDIR(st_mode):
raise OSError(errno.ENOTDIR, os.strerror(errno.ENOTDIR),
newpath)
path = newpath
continue
# The symlink is not resolved, so we must have a symlink loop.
if strict:
# Raise OSError(errno.ELOOP)
os.stat(newpath)
else:
# Return already resolved part + rest of the path unchanged.
return join(newpath, rest), False
seen[newpath] = None # not resolved symlink
path, ok = _joinrealpath(path, os.readlink(newpath), strict, seen)
if not ok:
return join(path, rest), False
seen[newpath] = path # resolved symlink
if newpath in seen:
# Already seen this path
path = seen[newpath]
if path is not None:
# use cached value
continue
# The symlink is not resolved, so we must have a symlink loop.
if strict:
# Raise OSError(errno.ELOOP)
os.stat(newpath)
path = newpath
continue
target = os.readlink(newpath)
except ignored_error:
pass
else:
# Resolve the symbolic link
if target.startswith(sep):
# Symlink target is absolute; reset resolved path.
path = sep
if maxlinks is None:
# Mark this symlink as seen but not fully resolved.
seen[newpath] = None
# Push the symlink path onto the stack, and signal its specialness
# by also pushing None. When these entries are popped, we'll
# record the fully-resolved symlink target in the 'seen' mapping.
rest.append(newpath)
rest.append(None)
# Push the unresolved symlink target parts onto the stack.
target_parts = target.split(sep)[::-1]
rest.extend(target_parts)
part_count += len(target_parts)
continue
# An error occurred and was ignored.
path = newpath
return path, True
return path
supports_unicode_filenames = (sys.platform == 'darwin')
@@ -505,10 +502,10 @@ supports_unicode_filenames = (sys.platform == 'darwin')
def relpath(path, start=None):
"""Return a relative version of a path"""
path = os.fspath(path)
if not path:
raise ValueError("no path specified")
path = os.fspath(path)
if isinstance(path, bytes):
curdir = b'.'
sep = b'/'
@@ -524,15 +521,17 @@ def relpath(path, start=None):
start = os.fspath(start)
try:
start_list = [x for x in abspath(start).split(sep) if x]
path_list = [x for x in abspath(path).split(sep) if x]
start_tail = abspath(start).lstrip(sep)
path_tail = abspath(path).lstrip(sep)
start_list = start_tail.split(sep) if start_tail else []
path_list = path_tail.split(sep) if path_tail else []
# Work out how much of the filepath is shared by start and path.
i = len(commonprefix([start_list, path_list]))
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
if not rel_list:
return curdir
return join(*rel_list)
return sep.join(rel_list)
except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
genericpath._check_arg_types('relpath', path, start)
raise
@@ -546,10 +545,11 @@ def relpath(path, start=None):
def commonpath(paths):
"""Given a sequence of path names, returns the longest common sub-path."""
paths = tuple(map(os.fspath, paths))
if not paths:
raise ValueError('commonpath() arg is an empty sequence')
paths = tuple(map(os.fspath, paths))
if isinstance(paths[0], bytes):
sep = b'/'
curdir = b'.'
@@ -561,7 +561,7 @@ def commonpath(paths):
split_paths = [path.split(sep) for path in paths]
try:
isabs, = set(p[:1] == sep for p in paths)
isabs, = {p.startswith(sep) for p in paths}
except ValueError:
raise ValueError("Can't mix absolute and relative paths") from None

View File

@@ -2,9 +2,12 @@ import inspect
import ntpath
import os
import string
import subprocess
import sys
import unittest
import warnings
from ntpath import ALLOW_MISSING
from test import support
from test.support import cpython_only, os_helper
from test.support import TestFailed, is_emscripten
from test.support.os_helper import FakePath
@@ -76,6 +79,10 @@ def tester(fn, wantResult):
%(str(fn), str(wantResult), repr(gotResult)))
def _parameterize(*parameters):
return support.subTests('kwargs', parameters, _do_cleanups=True)
class NtpathTestCase(unittest.TestCase):
def assertPathEqual(self, path1, path2):
if path1 == path2 or _norm(path1) == _norm(path2):
@@ -123,6 +130,24 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.splitdrive("//?/UNC/server/share/dir")',
("//?/UNC/server/share", "/dir"))
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_splitdrive_invalid_paths(self):
splitdrive = ntpath.splitdrive
self.assertEqual(splitdrive('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
('\\\\ser\x00ver\\sha\x00re', '\\di\x00r'))
self.assertEqual(splitdrive(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
(b'\\\\ser\x00ver\\sha\x00re', b'\\di\x00r'))
self.assertEqual(splitdrive("\\\\\udfff\\\udffe\\\udffd"),
('\\\\\udfff\\\udffe', '\\\udffd'))
if sys.platform == 'win32':
self.assertRaises(UnicodeDecodeError, splitdrive, b'\\\\\xff\\share\\dir')
self.assertRaises(UnicodeDecodeError, splitdrive, b'\\\\server\\\xff\\dir')
self.assertRaises(UnicodeDecodeError, splitdrive, b'\\\\server\\share\\\xff')
else:
self.assertEqual(splitdrive(b'\\\\\xff\\\xfe\\\xfd'),
(b'\\\\\xff\\\xfe', b'\\\xfd'))
def test_splitroot(self):
tester("ntpath.splitroot('')", ('', '', ''))
tester("ntpath.splitroot('foo')", ('', '', 'foo'))
@@ -213,6 +238,24 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.splitroot(" :/foo")', (" :", "/", "foo"))
tester('ntpath.splitroot("/:/foo")', ("", "/", ":/foo"))
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_splitroot_invalid_paths(self):
splitroot = ntpath.splitroot
self.assertEqual(splitroot('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
('\\\\ser\x00ver\\sha\x00re', '\\', 'di\x00r'))
self.assertEqual(splitroot(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
(b'\\\\ser\x00ver\\sha\x00re', b'\\', b'di\x00r'))
self.assertEqual(splitroot("\\\\\udfff\\\udffe\\\udffd"),
('\\\\\udfff\\\udffe', '\\', '\udffd'))
if sys.platform == 'win32':
self.assertRaises(UnicodeDecodeError, splitroot, b'\\\\\xff\\share\\dir')
self.assertRaises(UnicodeDecodeError, splitroot, b'\\\\server\\\xff\\dir')
self.assertRaises(UnicodeDecodeError, splitroot, b'\\\\server\\share\\\xff')
else:
self.assertEqual(splitroot(b'\\\\\xff\\\xfe\\\xfd'),
(b'\\\\\xff\\\xfe', b'\\', b'\xfd'))
def test_split(self):
tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
@@ -225,11 +268,36 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.split("c:/")', ('c:/', ''))
tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_split_invalid_paths(self):
split = ntpath.split
self.assertEqual(split('c:\\fo\x00o\\ba\x00r'),
('c:\\fo\x00o', 'ba\x00r'))
self.assertEqual(split(b'c:\\fo\x00o\\ba\x00r'),
(b'c:\\fo\x00o', b'ba\x00r'))
self.assertEqual(split('c:\\\udfff\\\udffe'),
('c:\\\udfff', '\udffe'))
if sys.platform == 'win32':
self.assertRaises(UnicodeDecodeError, split, b'c:\\\xff\\bar')
self.assertRaises(UnicodeDecodeError, split, b'c:\\foo\\\xff')
else:
self.assertEqual(split(b'c:\\\xff\\\xfe'),
(b'c:\\\xff', b'\xfe'))
def test_isabs(self):
tester('ntpath.isabs("foo\\bar")', 0)
tester('ntpath.isabs("foo/bar")', 0)
tester('ntpath.isabs("c:\\")', 1)
tester('ntpath.isabs("c:\\foo\\bar")', 1)
tester('ntpath.isabs("c:/foo/bar")', 1)
tester('ntpath.isabs("\\\\conky\\mountpoint\\")', 1)
tester('ntpath.isabs("\\foo")', 1)
tester('ntpath.isabs("\\foo\\bar")', 1)
# gh-44626: paths with only a drive or root are not absolute.
tester('ntpath.isabs("\\foo\\bar")', 0)
tester('ntpath.isabs("/foo/bar")', 0)
tester('ntpath.isabs("c:foo\\bar")', 0)
tester('ntpath.isabs("c:foo/bar")', 0)
# gh-96290: normal UNC paths and device paths without trailing backslashes
tester('ntpath.isabs("\\\\conky\\mountpoint")', 1)
@@ -255,6 +323,7 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c')
tester('ntpath.join("a", "b", "c\\")', 'a\\b\\c\\')
tester('ntpath.join("a", "b", "\\c")', '\\c')
tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
@@ -312,6 +381,44 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.join('\\\\computer\\', 'share')", '\\\\computer\\share')
tester("ntpath.join('\\\\computer\\share\\', 'a')", '\\\\computer\\share\\a')
tester("ntpath.join('\\\\computer\\share\\a\\', 'b')", '\\\\computer\\share\\a\\b')
# Second part is anchored, so that the first part is ignored.
tester("ntpath.join('a', 'Z:b', 'c')", 'Z:b\\c')
tester("ntpath.join('a', 'Z:\\b', 'c')", 'Z:\\b\\c')
tester("ntpath.join('a', '\\\\b\\c', 'd')", '\\\\b\\c\\d')
# Second part has a root but not drive.
tester("ntpath.join('a', '\\b', 'c')", '\\b\\c')
tester("ntpath.join('Z:/a', '/b', 'c')", 'Z:\\b\\c')
tester("ntpath.join('//?/Z:/a', '/b', 'c')", '\\\\?\\Z:\\b\\c')
tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b')
tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b')
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_normcase(self):
normcase = ntpath.normcase
self.assertEqual(normcase(''), '')
self.assertEqual(normcase(b''), b'')
self.assertEqual(normcase('ABC'), 'abc')
self.assertEqual(normcase(b'ABC'), b'abc')
self.assertEqual(normcase('\xc4\u0141\u03a8'), '\xe4\u0142\u03c8')
expected = '\u03c9\u2126' if sys.platform == 'win32' else '\u03c9\u03c9'
self.assertEqual(normcase('\u03a9\u2126'), expected)
if sys.platform == 'win32' or sys.getfilesystemencoding() == 'utf-8':
self.assertEqual(normcase('\xc4\u0141\u03a8'.encode()),
'\xe4\u0142\u03c8'.encode())
self.assertEqual(normcase('\u03a9\u2126'.encode()),
expected.encode())
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_normcase_invalid_paths(self):
normcase = ntpath.normcase
self.assertEqual(normcase('abc\x00def'), 'abc\x00def')
self.assertEqual(normcase(b'abc\x00def'), b'abc\x00def')
self.assertEqual(normcase('\udfff'), '\udfff')
if sys.platform == 'win32':
path = b'ABC' + bytes(range(128, 256))
self.assertEqual(normcase(path), path.lower())
def test_normpath(self):
tester("ntpath.normpath('A//////././//.//B')", r'A\B')
@@ -327,13 +434,18 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.normpath('..')", r'..')
tester("ntpath.normpath('.')", r'.')
tester("ntpath.normpath('c:.')", 'c:')
tester("ntpath.normpath('')", r'.')
tester("ntpath.normpath('/')", '\\')
tester("ntpath.normpath('c:/')", 'c:\\')
tester("ntpath.normpath('/../.././..')", '\\')
tester("ntpath.normpath('c:/../../..')", 'c:\\')
tester("ntpath.normpath('/./a/b')", r'\a\b')
tester("ntpath.normpath('c:/./a/b')", r'c:\a\b')
tester("ntpath.normpath('../.././..')", r'..\..\..')
tester("ntpath.normpath('K:../.././..')", r'K:..\..\..')
tester("ntpath.normpath('./a/b')", r'a\b')
tester("ntpath.normpath('c:./a/b')", r'c:a\b')
tester("ntpath.normpath('C:////a/b')", r'C:\a\b')
tester("ntpath.normpath('//machine/share//a/b')", r'\\machine\share\a\b')
@@ -354,6 +466,24 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.normpath('\\\\foo\\')", '\\\\foo\\')
tester("ntpath.normpath('\\\\foo')", '\\\\foo')
tester("ntpath.normpath('\\\\')", '\\\\')
tester("ntpath.normpath('//?/UNC/server/share/..')", '\\\\?\\UNC\\server\\share\\')
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_normpath_invalid_paths(self):
normpath = ntpath.normpath
self.assertEqual(normpath('fo\x00o'), 'fo\x00o')
self.assertEqual(normpath(b'fo\x00o'), b'fo\x00o')
self.assertEqual(normpath('fo\x00o\\..\\bar'), 'bar')
self.assertEqual(normpath(b'fo\x00o\\..\\bar'), b'bar')
self.assertEqual(normpath('\udfff'), '\udfff')
self.assertEqual(normpath('\udfff\\..\\foo'), 'foo')
if sys.platform == 'win32':
self.assertRaises(UnicodeDecodeError, normpath, b'\xff')
self.assertRaises(UnicodeDecodeError, normpath, b'\xff\\..\\foo')
else:
self.assertEqual(normpath(b'\xff'), b'\xff')
self.assertEqual(normpath(b'\xff\\..\\foo'), b'foo')
def test_realpath_curdir(self):
expected = ntpath.normpath(os.getcwd())
@@ -363,6 +493,27 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.realpath('.\\.')", expected)
tester("ntpath.realpath('\\'.join(['.'] * 100))", expected)
def test_realpath_curdir_strict(self):
expected = ntpath.normpath(os.getcwd())
tester("ntpath.realpath('.', strict=True)", expected)
tester("ntpath.realpath('./.', strict=True)", expected)
tester("ntpath.realpath('/'.join(['.'] * 100), strict=True)", expected)
tester("ntpath.realpath('.\\.', strict=True)", expected)
tester("ntpath.realpath('\\'.join(['.'] * 100), strict=True)", expected)
def test_realpath_curdir_missing_ok(self):
expected = ntpath.normpath(os.getcwd())
tester("ntpath.realpath('.', strict=ALLOW_MISSING)",
expected)
tester("ntpath.realpath('./.', strict=ALLOW_MISSING)",
expected)
tester("ntpath.realpath('/'.join(['.'] * 100), strict=ALLOW_MISSING)",
expected)
tester("ntpath.realpath('.\\.', strict=ALLOW_MISSING)",
expected)
tester("ntpath.realpath('\\'.join(['.'] * 100), strict=ALLOW_MISSING)",
expected)
def test_realpath_pardir(self):
expected = ntpath.normpath(os.getcwd())
tester("ntpath.realpath('..')", ntpath.dirname(expected))
@@ -375,28 +526,59 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.realpath('\\'.join(['..'] * 50))",
ntpath.splitdrive(expected)[0] + '\\')
def test_realpath_pardir_strict(self):
expected = ntpath.normpath(os.getcwd())
tester("ntpath.realpath('..', strict=True)", ntpath.dirname(expected))
tester("ntpath.realpath('../..', strict=True)",
ntpath.dirname(ntpath.dirname(expected)))
tester("ntpath.realpath('/'.join(['..'] * 50), strict=True)",
ntpath.splitdrive(expected)[0] + '\\')
tester("ntpath.realpath('..\\..', strict=True)",
ntpath.dirname(ntpath.dirname(expected)))
tester("ntpath.realpath('\\'.join(['..'] * 50), strict=True)",
ntpath.splitdrive(expected)[0] + '\\')
def test_realpath_pardir_missing_ok(self):
expected = ntpath.normpath(os.getcwd())
tester("ntpath.realpath('..', strict=ALLOW_MISSING)",
ntpath.dirname(expected))
tester("ntpath.realpath('../..', strict=ALLOW_MISSING)",
ntpath.dirname(ntpath.dirname(expected)))
tester("ntpath.realpath('/'.join(['..'] * 50), strict=ALLOW_MISSING)",
ntpath.splitdrive(expected)[0] + '\\')
tester("ntpath.realpath('..\\..', strict=ALLOW_MISSING)",
ntpath.dirname(ntpath.dirname(expected)))
tester("ntpath.realpath('\\'.join(['..'] * 50), strict=ALLOW_MISSING)",
ntpath.splitdrive(expected)[0] + '\\')
@os_helper.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_basic(self):
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_basic(self, kwargs):
ABSTFN = ntpath.abspath(os_helper.TESTFN)
open(ABSTFN, "wb").close()
self.addCleanup(os_helper.unlink, ABSTFN)
self.addCleanup(os_helper.unlink, ABSTFN + "1")
os.symlink(ABSTFN, ABSTFN + "1")
self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN)
self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1")),
self.assertPathEqual(ntpath.realpath(ABSTFN + "1", **kwargs), ABSTFN)
self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1"), **kwargs),
os.fsencode(ABSTFN))
# gh-88013: call ntpath.realpath with binary drive name may raise a
# TypeError. The drive should not exist to reproduce the bug.
drives = {f"{c}:\\" for c in string.ascii_uppercase} - set(os.listdrives())
d = drives.pop().encode()
self.assertEqual(ntpath.realpath(d), d)
self.assertEqual(ntpath.realpath(d, strict=False), d)
# gh-106242: Embedded nulls and non-strict fallback to abspath
self.assertEqual(ABSTFN + "\0spam",
ntpath.realpath(os_helper.TESTFN + "\0spam", strict=False))
if kwargs:
with self.assertRaises(OSError):
ntpath.realpath(os_helper.TESTFN + "\0spam",
**kwargs)
else:
self.assertEqual(ABSTFN + "\0spam",
ntpath.realpath(os_helper.TESTFN + "\0spam", **kwargs))
@os_helper.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
@@ -408,19 +590,77 @@ class TestNtpath(NtpathTestCase):
self.addCleanup(os_helper.unlink, ABSTFN)
self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, strict=True)
self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", strict=True)
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_invalid_paths(self):
realpath = ntpath.realpath
ABSTFN = ntpath.abspath(os_helper.TESTFN)
ABSTFNb = os.fsencode(ABSTFN)
path = ABSTFN + '\x00'
# gh-106242: Embedded nulls and non-strict fallback to abspath
self.assertEqual(realpath(path, strict=False), path)
# gh-106242: Embedded nulls should raise OSError (not ValueError)
self.assertRaises(OSError, ntpath.realpath, ABSTFN + "\0spam", strict=True)
self.assertRaises(OSError, realpath, path, strict=True)
self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING)
path = ABSTFNb + b'\x00'
self.assertEqual(realpath(path, strict=False), path)
self.assertRaises(OSError, realpath, path, strict=True)
self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING)
path = ABSTFN + '\\nonexistent\\x\x00'
self.assertEqual(realpath(path, strict=False), path)
self.assertRaises(OSError, realpath, path, strict=True)
self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING)
path = ABSTFNb + b'\\nonexistent\\x\x00'
self.assertEqual(realpath(path, strict=False), path)
self.assertRaises(OSError, realpath, path, strict=True)
self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING)
path = ABSTFN + '\x00\\..'
self.assertEqual(realpath(path, strict=False), os.getcwd())
self.assertEqual(realpath(path, strict=True), os.getcwd())
self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwd())
path = ABSTFNb + b'\x00\\..'
self.assertEqual(realpath(path, strict=False), os.getcwdb())
self.assertEqual(realpath(path, strict=True), os.getcwdb())
self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwdb())
path = ABSTFN + '\\nonexistent\\x\x00\\..'
self.assertEqual(realpath(path, strict=False), ABSTFN + '\\nonexistent')
self.assertRaises(OSError, realpath, path, strict=True)
self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFN + '\\nonexistent')
path = ABSTFNb + b'\\nonexistent\\x\x00\\..'
self.assertEqual(realpath(path, strict=False), ABSTFNb + b'\\nonexistent')
self.assertRaises(OSError, realpath, path, strict=True)
self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFNb + b'\\nonexistent')
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_invalid_unicode_paths(self, kwargs):
realpath = ntpath.realpath
ABSTFN = ntpath.abspath(os_helper.TESTFN)
ABSTFNb = os.fsencode(ABSTFN)
path = ABSTFNb + b'\xff'
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
path = ABSTFNb + b'\\nonexistent\\\xff'
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
path = ABSTFNb + b'\xff\\..'
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
path = ABSTFNb + b'\\nonexistent\\\xff\\..'
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs)
@os_helper.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_relative(self):
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_relative(self, kwargs):
ABSTFN = ntpath.abspath(os_helper.TESTFN)
open(ABSTFN, "wb").close()
self.addCleanup(os_helper.unlink, ABSTFN)
self.addCleanup(os_helper.unlink, ABSTFN + "1")
os.symlink(ABSTFN, ntpath.relpath(ABSTFN + "1"))
self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN)
self.assertPathEqual(ntpath.realpath(ABSTFN + "1", **kwargs), ABSTFN)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@@ -574,7 +814,62 @@ class TestNtpath(NtpathTestCase):
@os_helper.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_symlink_prefix(self):
def test_realpath_symlink_loops_raise(self):
# Symlink loops raise OSError in ALLOW_MISSING mode
ABSTFN = ntpath.abspath(os_helper.TESTFN)
self.addCleanup(os_helper.unlink, ABSTFN)
self.addCleanup(os_helper.unlink, ABSTFN + "1")
self.addCleanup(os_helper.unlink, ABSTFN + "2")
self.addCleanup(os_helper.unlink, ABSTFN + "y")
self.addCleanup(os_helper.unlink, ABSTFN + "c")
self.addCleanup(os_helper.unlink, ABSTFN + "a")
self.addCleanup(os_helper.unlink, ABSTFN + "x")
os.symlink(ABSTFN, ABSTFN)
self.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=ALLOW_MISSING)
os.symlink(ABSTFN + "1", ABSTFN + "2")
os.symlink(ABSTFN + "2", ABSTFN + "1")
self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1",
strict=ALLOW_MISSING)
self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2",
strict=ALLOW_MISSING)
self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x",
strict=ALLOW_MISSING)
# Windows eliminates '..' components before resolving links;
# realpath is not expected to raise if this removes the loop.
self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\.."),
ntpath.dirname(ABSTFN))
self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\x"),
ntpath.dirname(ABSTFN) + "\\x")
os.symlink(ABSTFN + "x", ABSTFN + "y")
self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\"
+ ntpath.basename(ABSTFN) + "y"),
ABSTFN + "x")
self.assertRaises(
OSError, ntpath.realpath,
ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1",
strict=ALLOW_MISSING)
os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a",
strict=ALLOW_MISSING)
os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
+ "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c",
strict=ALLOW_MISSING)
# Test using relative path as well.
self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN),
strict=ALLOW_MISSING)
@os_helper.skip_unless_symlink
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_symlink_prefix(self, kwargs):
ABSTFN = ntpath.abspath(os_helper.TESTFN)
self.addCleanup(os_helper.unlink, ABSTFN + "3")
self.addCleanup(os_helper.unlink, "\\\\?\\" + ABSTFN + "3.")
@@ -589,9 +884,9 @@ class TestNtpath(NtpathTestCase):
f.write(b'1')
os.symlink("\\\\?\\" + ABSTFN + "3.", ABSTFN + "3.link")
self.assertPathEqual(ntpath.realpath(ABSTFN + "3link"),
self.assertPathEqual(ntpath.realpath(ABSTFN + "3link", **kwargs),
ABSTFN + "3")
self.assertPathEqual(ntpath.realpath(ABSTFN + "3.link"),
self.assertPathEqual(ntpath.realpath(ABSTFN + "3.link", **kwargs),
"\\\\?\\" + ABSTFN + "3.")
# Resolved paths should be usable to open target files
@@ -601,14 +896,17 @@ class TestNtpath(NtpathTestCase):
self.assertEqual(f.read(), b'1')
# When the prefix is included, it is not stripped
self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3link"),
self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3link", **kwargs),
"\\\\?\\" + ABSTFN + "3")
self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link"),
self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link", **kwargs),
"\\\\?\\" + ABSTFN + "3.")
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_nul(self):
tester("ntpath.realpath('NUL')", r'\\.\NUL')
tester("ntpath.realpath('NUL', strict=False)", r'\\.\NUL')
tester("ntpath.realpath('NUL', strict=True)", r'\\.\NUL')
tester("ntpath.realpath('NUL', strict=ALLOW_MISSING)", r'\\.\NUL')
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
@unittest.skipUnless(HAVE_GETSHORTPATHNAME, 'need _getshortpathname')
@@ -632,13 +930,67 @@ class TestNtpath(NtpathTestCase):
self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short))
with os_helper.change_cwd(test_dir_long):
self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
with os_helper.change_cwd(test_dir_long.lower()):
self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
with os_helper.change_cwd(test_dir_short):
self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
for kwargs in {}, {'strict': True}, {'strict': ALLOW_MISSING}:
with self.subTest(**kwargs):
with os_helper.change_cwd(test_dir_long):
self.assertPathEqual(
test_file_long,
ntpath.realpath("file.txt", **kwargs))
with os_helper.change_cwd(test_dir_long.lower()):
self.assertPathEqual(
test_file_long,
ntpath.realpath("file.txt", **kwargs))
with os_helper.change_cwd(test_dir_short):
self.assertPathEqual(
test_file_long,
ntpath.realpath("file.txt", **kwargs))
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_permission(self):
# Test whether python can resolve the real filename of a
# shortened file name even if it does not have permission to access it.
ABSTFN = ntpath.realpath(os_helper.TESTFN)
os_helper.unlink(ABSTFN)
os_helper.rmtree(ABSTFN)
os.mkdir(ABSTFN)
self.addCleanup(os_helper.rmtree, ABSTFN)
test_file = ntpath.join(ABSTFN, "LongFileName123.txt")
test_file_short = ntpath.join(ABSTFN, "LONGFI~1.TXT")
with open(test_file, "wb") as f:
f.write(b"content")
# Automatic generation of short names may be disabled on
# NTFS volumes for the sake of performance.
# They're not supported at all on ReFS and exFAT.
p = subprocess.run(
# Try to set the short name manually.
['fsutil.exe', 'file', 'setShortName', test_file, 'LONGFI~1.TXT'],
creationflags=subprocess.DETACHED_PROCESS
)
if p.returncode:
raise unittest.SkipTest('failed to set short name')
try:
self.assertPathEqual(test_file, ntpath.realpath(test_file_short))
except AssertionError:
raise unittest.SkipTest('the filesystem seems to lack support for short filenames')
# Deny the right to [S]YNCHRONIZE on the file to
# force nt._getfinalpathname to fail with ERROR_ACCESS_DENIED.
p = subprocess.run(
['icacls.exe', test_file, '/deny', '*S-1-5-32-545:(S)'],
creationflags=subprocess.DETACHED_PROCESS
)
if p.returncode:
raise unittest.SkipTest('failed to deny access to the test file')
self.assertPathEqual(test_file, ntpath.realpath(test_file_short))
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; ValueError: illegal environment variable name")
def test_expandvars(self):
with os_helper.EnvironmentVarGuard() as env:
@@ -666,6 +1018,7 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar")
tester('ntpath.expandvars("bar\'%foo%")', "bar\'%foo%")
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; ValueError: illegal environment variable name")
@unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
def test_expandvars_nonascii(self):
@@ -687,6 +1040,7 @@ class TestNtpath(NtpathTestCase):
check('%spam%bar', '%sbar' % nonascii)
check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_expanduser(self):
tester('ntpath.expanduser("test")', 'test')
@@ -748,6 +1102,7 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.abspath("C:\\spam. . .")', "C:\\spam")
tester('ntpath.abspath("C:/nul")', "\\\\.\\nul")
tester('ntpath.abspath("C:\\nul")', "\\\\.\\nul")
self.assertTrue(ntpath.isabs(ntpath.abspath("C:spam")))
tester('ntpath.abspath("//..")', "\\\\")
tester('ntpath.abspath("//../")', "\\\\..\\")
tester('ntpath.abspath("//../..")', "\\\\..\\")
@@ -781,6 +1136,28 @@ class TestNtpath(NtpathTestCase):
drive, _ = ntpath.splitdrive(cwd_dir)
tester('ntpath.abspath("/abc/")', drive + "\\abc")
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_abspath_invalid_paths(self):
abspath = ntpath.abspath
if sys.platform == 'win32':
self.assertEqual(abspath("C:\x00"), ntpath.join(abspath("C:"), "\x00"))
self.assertEqual(abspath(b"C:\x00"), ntpath.join(abspath(b"C:"), b"\x00"))
self.assertEqual(abspath("\x00:spam"), "\x00:\\spam")
self.assertEqual(abspath(b"\x00:spam"), b"\x00:\\spam")
self.assertEqual(abspath('c:\\fo\x00o'), 'c:\\fo\x00o')
self.assertEqual(abspath(b'c:\\fo\x00o'), b'c:\\fo\x00o')
self.assertEqual(abspath('c:\\fo\x00o\\..\\bar'), 'c:\\bar')
self.assertEqual(abspath(b'c:\\fo\x00o\\..\\bar'), b'c:\\bar')
self.assertEqual(abspath('c:\\\udfff'), 'c:\\\udfff')
self.assertEqual(abspath('c:\\\udfff\\..\\foo'), 'c:\\foo')
if sys.platform == 'win32':
self.assertRaises(UnicodeDecodeError, abspath, b'c:\\\xff')
self.assertRaises(UnicodeDecodeError, abspath, b'c:\\\xff\\..\\foo')
else:
self.assertEqual(abspath(b'c:\\\xff'), b'c:\\\xff')
self.assertEqual(abspath(b'c:\\\xff\\..\\foo'), b'c:\\foo')
def test_relpath(self):
tester('ntpath.relpath("a")', 'a')
tester('ntpath.relpath(ntpath.abspath("a"))', 'a')
@@ -809,43 +1186,47 @@ class TestNtpath(NtpathTestCase):
def check(paths, expected):
tester(('ntpath.commonpath(%r)' % paths).replace('\\\\', '\\'),
expected)
def check_error(exc, paths):
self.assertRaises(exc, ntpath.commonpath, paths)
self.assertRaises(exc, ntpath.commonpath,
[os.fsencode(p) for p in paths])
def check_error(paths, expected):
self.assertRaisesRegex(ValueError, expected, ntpath.commonpath, paths)
self.assertRaisesRegex(ValueError, expected, ntpath.commonpath, paths[::-1])
self.assertRaisesRegex(ValueError, expected, ntpath.commonpath,
[os.fsencode(p) for p in paths])
self.assertRaisesRegex(ValueError, expected, ntpath.commonpath,
[os.fsencode(p) for p in paths[::-1]])
self.assertRaises(TypeError, ntpath.commonpath, None)
self.assertRaises(ValueError, ntpath.commonpath, [])
check_error(ValueError, ['C:\\Program Files', 'Program Files'])
check_error(ValueError, ['C:\\Program Files', 'C:Program Files'])
check_error(ValueError, ['\\Program Files', 'Program Files'])
check_error(ValueError, ['Program Files', 'C:\\Program Files'])
check(['C:\\Program Files'], 'C:\\Program Files')
check(['C:\\Program Files', 'C:\\Program Files'], 'C:\\Program Files')
check(['C:\\Program Files\\', 'C:\\Program Files'],
'C:\\Program Files')
check(['C:\\Program Files\\', 'C:\\Program Files\\'],
'C:\\Program Files')
check(['C:\\\\Program Files', 'C:\\Program Files\\\\'],
'C:\\Program Files')
check(['C:\\.\\Program Files', 'C:\\Program Files\\.'],
'C:\\Program Files')
check(['C:\\', 'C:\\bin'], 'C:\\')
check(['C:\\Program Files', 'C:\\bin'], 'C:\\')
check(['C:\\Program Files', 'C:\\Program Files\\Bar'],
'C:\\Program Files')
check(['C:\\Program Files\\Foo', 'C:\\Program Files\\Bar'],
'C:\\Program Files')
check(['C:\\Program Files', 'C:\\Projects'], 'C:\\')
check(['C:\\Program Files\\', 'C:\\Projects'], 'C:\\')
self.assertRaises(ValueError, ntpath.commonpath, iter([]))
check(['C:\\Program Files\\Foo', 'C:/Program Files/Bar'],
'C:\\Program Files')
check(['C:\\Program Files\\Foo', 'c:/program files/bar'],
'C:\\Program Files')
check(['c:/program files/bar', 'C:\\Program Files\\Foo'],
'c:\\program files')
# gh-117381: Logical error messages
check_error(['C:\\Foo', 'C:Foo'], "Can't mix absolute and relative paths")
check_error(['C:\\Foo', '\\Foo'], "Paths don't have the same drive")
check_error(['C:\\Foo', 'Foo'], "Paths don't have the same drive")
check_error(['C:Foo', '\\Foo'], "Paths don't have the same drive")
check_error(['C:Foo', 'Foo'], "Paths don't have the same drive")
check_error(['\\Foo', 'Foo'], "Can't mix rooted and not-rooted paths")
check_error(ValueError, ['C:\\Program Files', 'D:\\Program Files'])
check(['C:\\Foo'], 'C:\\Foo')
check(['C:\\Foo', 'C:\\Foo'], 'C:\\Foo')
check(['C:\\Foo\\', 'C:\\Foo'], 'C:\\Foo')
check(['C:\\Foo\\', 'C:\\Foo\\'], 'C:\\Foo')
check(['C:\\\\Foo', 'C:\\Foo\\\\'], 'C:\\Foo')
check(['C:\\.\\Foo', 'C:\\Foo\\.'], 'C:\\Foo')
check(['C:\\', 'C:\\baz'], 'C:\\')
check(['C:\\Bar', 'C:\\baz'], 'C:\\')
check(['C:\\Foo', 'C:\\Foo\\Baz'], 'C:\\Foo')
check(['C:\\Foo\\Bar', 'C:\\Foo\\Baz'], 'C:\\Foo')
check(['C:\\Bar', 'C:\\Baz'], 'C:\\')
check(['C:\\Bar\\', 'C:\\Baz'], 'C:\\')
check(['C:\\Foo\\Bar', 'C:/Foo/Baz'], 'C:\\Foo')
check(['C:\\Foo\\Bar', 'c:/foo/baz'], 'C:\\Foo')
check(['c:/foo/bar', 'C:\\Foo\\Baz'], 'c:\\foo')
# gh-117381: Logical error messages
check_error(['C:\\Foo', 'D:\\Foo'], "Paths don't have the same drive")
check_error(['C:\\Foo', 'D:Foo'], "Paths don't have the same drive")
check_error(['C:Foo', 'D:Foo'], "Paths don't have the same drive")
check(['spam'], 'spam')
check(['spam', 'spam'], 'spam')
@@ -859,20 +1240,16 @@ class TestNtpath(NtpathTestCase):
check([''], '')
check(['', 'spam\\alot'], '')
check_error(ValueError, ['', '\\spam\\alot'])
self.assertRaises(TypeError, ntpath.commonpath,
[b'C:\\Program Files', 'C:\\Program Files\\Foo'])
self.assertRaises(TypeError, ntpath.commonpath,
[b'C:\\Program Files', 'Program Files\\Foo'])
self.assertRaises(TypeError, ntpath.commonpath,
[b'Program Files', 'C:\\Program Files\\Foo'])
self.assertRaises(TypeError, ntpath.commonpath,
['C:\\Program Files', b'C:\\Program Files\\Foo'])
self.assertRaises(TypeError, ntpath.commonpath,
['C:\\Program Files', b'Program Files\\Foo'])
self.assertRaises(TypeError, ntpath.commonpath,
['Program Files', b'C:\\Program Files\\Foo'])
# gh-117381: Logical error messages
check_error(['', '\\spam\\alot'], "Can't mix rooted and not-rooted paths")
self.assertRaises(TypeError, ntpath.commonpath, [b'C:\\Foo', 'C:\\Foo\\Baz'])
self.assertRaises(TypeError, ntpath.commonpath, [b'C:\\Foo', 'Foo\\Baz'])
self.assertRaises(TypeError, ntpath.commonpath, [b'Foo', 'C:\\Foo\\Baz'])
self.assertRaises(TypeError, ntpath.commonpath, ['C:\\Foo', b'C:\\Foo\\Baz'])
self.assertRaises(TypeError, ntpath.commonpath, ['C:\\Foo', b'Foo\\Baz'])
self.assertRaises(TypeError, ntpath.commonpath, ['Foo', b'C:\\Foo\\Baz'])
@unittest.skipIf(is_emscripten, "Emscripten cannot fstat unnamed files.")
def test_sameopenfile(self):
@@ -924,6 +1301,76 @@ class TestNtpath(NtpathTestCase):
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
# TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'win32', "TODO: RUSTPYTHON; crash")
def test_ismount_invalid_paths(self):
ismount = ntpath.ismount
self.assertFalse(ismount("c:\\\udfff"))
if sys.platform == 'win32':
self.assertRaises(ValueError, ismount, "c:\\\x00")
self.assertRaises(ValueError, ismount, b"c:\\\x00")
self.assertRaises(UnicodeDecodeError, ismount, b"c:\\\xff")
else:
self.assertFalse(ismount("c:\\\x00"))
self.assertFalse(ismount(b"c:\\\x00"))
self.assertFalse(ismount(b"c:\\\xff"))
def test_isreserved(self):
self.assertFalse(ntpath.isreserved(''))
self.assertFalse(ntpath.isreserved('.'))
self.assertFalse(ntpath.isreserved('..'))
self.assertFalse(ntpath.isreserved('/'))
self.assertFalse(ntpath.isreserved('/foo/bar'))
# A name that ends with a space or dot is reserved.
self.assertTrue(ntpath.isreserved('foo.'))
self.assertTrue(ntpath.isreserved('foo '))
# ASCII control characters are reserved.
self.assertTrue(ntpath.isreserved('\foo'))
# Wildcard characters, colon, and pipe are reserved.
self.assertTrue(ntpath.isreserved('foo*bar'))
self.assertTrue(ntpath.isreserved('foo?bar'))
self.assertTrue(ntpath.isreserved('foo"bar'))
self.assertTrue(ntpath.isreserved('foo<bar'))
self.assertTrue(ntpath.isreserved('foo>bar'))
self.assertTrue(ntpath.isreserved('foo:bar'))
self.assertTrue(ntpath.isreserved('foo|bar'))
# Case-insensitive DOS-device names are reserved.
self.assertTrue(ntpath.isreserved('nul'))
self.assertTrue(ntpath.isreserved('aux'))
self.assertTrue(ntpath.isreserved('prn'))
self.assertTrue(ntpath.isreserved('con'))
self.assertTrue(ntpath.isreserved('conin$'))
self.assertTrue(ntpath.isreserved('conout$'))
# COM/LPT + 1-9 or + superscript 1-3 are reserved.
self.assertTrue(ntpath.isreserved('COM1'))
self.assertTrue(ntpath.isreserved('LPT9'))
self.assertTrue(ntpath.isreserved('com\xb9'))
self.assertTrue(ntpath.isreserved('com\xb2'))
self.assertTrue(ntpath.isreserved('lpt\xb3'))
# DOS-device name matching ignores characters after a dot or
# a colon and also ignores trailing spaces.
self.assertTrue(ntpath.isreserved('NUL.txt'))
self.assertTrue(ntpath.isreserved('PRN '))
self.assertTrue(ntpath.isreserved('AUX .txt'))
self.assertTrue(ntpath.isreserved('COM1:bar'))
self.assertTrue(ntpath.isreserved('LPT9 :bar'))
# DOS-device names are only matched at the beginning
# of a path component.
self.assertFalse(ntpath.isreserved('bar.com9'))
self.assertFalse(ntpath.isreserved('bar.lpt9'))
# The entire path is checked, except for the drive.
self.assertTrue(ntpath.isreserved('c:/bar/baz/NUL'))
self.assertTrue(ntpath.isreserved('c:/NUL/bar/baz'))
self.assertFalse(ntpath.isreserved('//./NUL'))
# Bytes are supported.
self.assertFalse(ntpath.isreserved(b''))
self.assertFalse(ntpath.isreserved(b'.'))
self.assertFalse(ntpath.isreserved(b'..'))
self.assertFalse(ntpath.isreserved(b'/'))
self.assertFalse(ntpath.isreserved(b'/foo/bar'))
self.assertTrue(ntpath.isreserved(b'foo.'))
self.assertTrue(ntpath.isreserved(b'nul'))
def assertEqualCI(self, s1, s2):
"""Assert that two strings are equal ignoring case differences."""
self.assertEqual(s1.lower(), s2.lower())
@@ -974,6 +1421,13 @@ class TestNtpath(NtpathTestCase):
self.assertFalse(ntpath.isjunction('tmpdir'))
self.assertPathEqual(ntpath.realpath('testjunc'), ntpath.realpath('tmpdir'))
def test_isfile_invalid_paths(self):
isfile = ntpath.isfile
self.assertIs(isfile('/tmp\udfffabcds'), False)
self.assertIs(isfile(b'/tmp\xffabcds'), False)
self.assertIs(isfile('/tmp\x00abcds'), False)
self.assertIs(isfile(b'/tmp\x00abcds'), False)
@unittest.skipIf(sys.platform != 'win32', "drive letters are a windows concept")
def test_isfile_driveletter(self):
drive = os.environ.get('SystemDrive')
@@ -981,6 +1435,29 @@ class TestNtpath(NtpathTestCase):
raise unittest.SkipTest('SystemDrive is not defined or malformed')
self.assertFalse(os.path.isfile('\\\\.\\' + drive))
@unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()")
def test_isfile_anonymous_pipe(self):
pr, pw = os.pipe()
try:
self.assertFalse(ntpath.isfile(pr))
finally:
os.close(pr)
os.close(pw)
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
@unittest.skipIf(sys.platform != 'win32', "windows only")
def test_isfile_named_pipe(self):
import _winapi
named_pipe = f'//./PIPE/python_isfile_test_{os.getpid()}'
h = _winapi.CreateNamedPipe(named_pipe,
_winapi.PIPE_ACCESS_INBOUND,
0, 1, 0, 0, 0, 0)
try:
self.assertFalse(ntpath.isfile(named_pipe))
finally:
_winapi.CloseHandle(h)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(sys.platform != 'win32', "windows only")
@@ -996,14 +1473,22 @@ class TestNtpath(NtpathTestCase):
# There are fast paths of these functions implemented in posixmodule.c.
# Confirm that they are being used, and not the Python fallbacks in
# genericpath.py.
self.assertTrue(os.path.splitroot is nt._path_splitroot_ex)
self.assertFalse(inspect.isfunction(os.path.splitroot))
self.assertTrue(os.path.normpath is nt._path_normpath)
self.assertFalse(inspect.isfunction(os.path.normpath))
self.assertTrue(os.path.isdir is nt._path_isdir)
self.assertFalse(inspect.isfunction(os.path.isdir))
self.assertTrue(os.path.isfile is nt._path_isfile)
self.assertFalse(inspect.isfunction(os.path.isfile))
self.assertTrue(os.path.islink is nt._path_islink)
self.assertFalse(inspect.isfunction(os.path.islink))
self.assertTrue(os.path.isjunction is nt._path_isjunction)
self.assertFalse(inspect.isfunction(os.path.isjunction))
self.assertTrue(os.path.exists is nt._path_exists)
self.assertFalse(inspect.isfunction(os.path.exists))
self.assertTrue(os.path.lexists is nt._path_lexists)
self.assertFalse(inspect.isfunction(os.path.lexists))
# TODO: RUSTPYTHON
@unittest.expectedFailure
@@ -1033,16 +1518,14 @@ class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
attributes = ['relpath']
# TODO: RUSTPYTHON
if sys.platform == "win32":
# TODO: RUSTPYTHON, ValueError: illegal environment variable name
@unittest.expectedFailure
def test_expandvars(self): # TODO: RUSTPYTHON; remove when done
super().test_expandvars()
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; ValueError: illegal environment variable name")
def test_expandvars(self):
return super().test_expandvars()
# TODO: RUSTPYTHON, ValueError: illegal environment variable name
@unittest.expectedFailure
def test_expandvars_nonascii(self): # TODO: RUSTPYTHON; remove when done
super().test_expandvars_nonascii()
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; ValueError: illegal environment variable name")
def test_expandvars_nonascii(self):
return super().test_expandvars_nonascii()
class PathLikeTests(NtpathTestCase):
@@ -1059,12 +1542,8 @@ class PathLikeTests(NtpathTestCase):
def _check_function(self, func):
self.assertPathEqual(func(self.file_path), func(self.file_name))
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: 'ωω' != 'ωΩ'")
def test_path_normcase(self):
self._check_function(self.path.normcase)
if sys.platform == 'win32':
self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
def test_path_isabs(self):
self._check_function(self.path.isabs)

View File

@@ -1,12 +1,16 @@
import inspect
import os
import posixpath
import random
import sys
import unittest
from posixpath import realpath, abspath, dirname, basename
from functools import partial
from posixpath import realpath, abspath, dirname, basename, ALLOW_MISSING
from test import support
from test import test_genericpath
from test.support import import_helper
from test.support import os_helper
from test.support.os_helper import FakePath
from test.support.os_helper import FakePath, TESTFN
from unittest import mock
try:
@@ -18,7 +22,7 @@ except ImportError:
# An absolute path to a temporary filename for testing. We can't rely on TESTFN
# being an absolute path, so we need this.
ABSTFN = abspath(os_helper.TESTFN)
ABSTFN = abspath(TESTFN)
def skip_if_ABSTFN_contains_backslash(test):
"""
@@ -30,35 +34,40 @@ def skip_if_ABSTFN_contains_backslash(test):
msg = "ABSTFN is not a posix path - tests fail"
return [test, unittest.skip(msg)(test)][found_backslash]
def safe_rmdir(dirname):
try:
os.rmdir(dirname)
except OSError:
pass
def _parameterize(*parameters):
return support.subTests('kwargs', parameters)
class PosixPathTest(unittest.TestCase):
def setUp(self):
self.tearDown()
def tearDown(self):
for suffix in ["", "1", "2"]:
os_helper.unlink(os_helper.TESTFN + suffix)
safe_rmdir(os_helper.TESTFN + suffix)
self.assertFalse(posixpath.lexists(ABSTFN + suffix))
def test_join(self):
self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"),
"/bar/baz")
self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz")
self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"),
"/foo/bar/baz/")
fn = posixpath.join
self.assertEqual(fn("/foo", "bar", "/bar", "baz"), "/bar/baz")
self.assertEqual(fn("/foo", "bar", "baz"), "/foo/bar/baz")
self.assertEqual(fn("/foo/", "bar/", "baz/"), "/foo/bar/baz/")
self.assertEqual(posixpath.join(b"/foo", b"bar", b"/bar", b"baz"),
b"/bar/baz")
self.assertEqual(posixpath.join(b"/foo", b"bar", b"baz"),
b"/foo/bar/baz")
self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"),
b"/foo/bar/baz/")
self.assertEqual(fn(b"/foo", b"bar", b"/bar", b"baz"), b"/bar/baz")
self.assertEqual(fn(b"/foo", b"bar", b"baz"), b"/foo/bar/baz")
self.assertEqual(fn(b"/foo/", b"bar/", b"baz/"), b"/foo/bar/baz/")
self.assertEqual(fn("a", ""), "a/")
self.assertEqual(fn("a", "", ""), "a/")
self.assertEqual(fn("a", "b"), "a/b")
self.assertEqual(fn("a", "b/"), "a/b/")
self.assertEqual(fn("a/", "b"), "a/b")
self.assertEqual(fn("a/", "b/"), "a/b/")
self.assertEqual(fn("a", "b/c", "d"), "a/b/c/d")
self.assertEqual(fn("a", "b//c", "d"), "a/b//c/d")
self.assertEqual(fn("a", "b/c/", "d"), "a/b/c/d")
self.assertEqual(fn("/a", "b"), "/a/b")
self.assertEqual(fn("/a/", "b"), "/a/b")
self.assertEqual(fn("a", "/b", "c"), "/b/c")
self.assertEqual(fn("a", "/b", "/c"), "/c")
def test_split(self):
self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar"))
@@ -180,27 +189,31 @@ class PosixPathTest(unittest.TestCase):
self.assertEqual(posixpath.dirname(b"////foo"), b"////")
self.assertEqual(posixpath.dirname(b"//foo//bar"), b"//foo")
@unittest.expectedFailureIf(os.name == "nt", "TODO: RUSTPYTHON")
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_islink(self):
self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False)
self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), False)
self.assertIs(posixpath.islink(TESTFN + "1"), False)
self.assertIs(posixpath.lexists(TESTFN + "2"), False)
with open(os_helper.TESTFN + "1", "wb") as f:
self.addCleanup(os_helper.unlink, TESTFN + "1")
with open(TESTFN + "1", "wb") as f:
f.write(b"foo")
self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False)
self.assertIs(posixpath.islink(TESTFN + "1"), False)
if os_helper.can_symlink():
os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN + "2")
self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True)
os.remove(os_helper.TESTFN + "1")
self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True)
self.assertIs(posixpath.exists(os_helper.TESTFN + "2"), False)
self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), True)
self.addCleanup(os_helper.unlink, TESTFN + "2")
os.symlink(TESTFN + "1", TESTFN + "2")
self.assertIs(posixpath.islink(TESTFN + "2"), True)
os.remove(TESTFN + "1")
self.assertIs(posixpath.islink(TESTFN + "2"), True)
self.assertIs(posixpath.exists(TESTFN + "2"), False)
self.assertIs(posixpath.lexists(TESTFN + "2"), True)
self.assertIs(posixpath.islink(os_helper.TESTFN + "\udfff"), False)
self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\xff"), False)
self.assertIs(posixpath.islink(os_helper.TESTFN + "\x00"), False)
self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\x00"), False)
def test_islink_invalid_paths(self):
self.assertIs(posixpath.islink(TESTFN + "\udfff"), False)
self.assertIs(posixpath.islink(os.fsencode(TESTFN) + b"\xff"), False)
self.assertIs(posixpath.islink(TESTFN + "\x00"), False)
self.assertIs(posixpath.islink(os.fsencode(TESTFN) + b"\x00"), False)
def test_ismount(self):
self.assertIs(posixpath.ismount("/"), True)
@@ -215,13 +228,15 @@ class PosixPathTest(unittest.TestCase):
os.mkdir(ABSTFN)
self.assertIs(posixpath.ismount(ABSTFN), False)
finally:
safe_rmdir(ABSTFN)
os_helper.rmdir(ABSTFN)
def test_ismount_invalid_paths(self):
self.assertIs(posixpath.ismount('/\udfff'), False)
self.assertIs(posixpath.ismount(b'/\xff'), False)
self.assertIs(posixpath.ismount('/\x00'), False)
self.assertIs(posixpath.ismount(b'/\x00'), False)
# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
@os_helper.skip_unless_symlink
def test_ismount_symlinks(self):
@@ -230,7 +245,7 @@ class PosixPathTest(unittest.TestCase):
os.symlink("/", ABSTFN)
self.assertIs(posixpath.ismount(ABSTFN), False)
finally:
os.unlink(ABSTFN)
os_helper.unlink(ABSTFN)
@unittest.skipIf(posix is None, "Test requires posix module")
def test_ismount_different_device(self):
@@ -275,6 +290,16 @@ class PosixPathTest(unittest.TestCase):
def test_isjunction(self):
self.assertFalse(posixpath.isjunction(ABSTFN))
@unittest.skipIf(sys.platform == 'win32', "Fast paths are not for win32")
@support.cpython_only
def test_fast_paths_in_use(self):
# There are fast paths of these functions implemented in posixmodule.c.
# Confirm that they are being used, and not the Python fallbacks
self.assertTrue(os.path.splitroot is posix._path_splitroot_ex)
self.assertFalse(inspect.isfunction(os.path.splitroot))
self.assertTrue(os.path.normpath is posix._path_normpath)
self.assertFalse(inspect.isfunction(os.path.normpath))
def test_expanduser(self):
self.assertEqual(posixpath.expanduser("foo"), "foo")
self.assertEqual(posixpath.expanduser(b"foo"), b"foo")
@@ -336,12 +361,38 @@ class PosixPathTest(unittest.TestCase):
for path in ('~', '~/.local', '~vstinner/'):
self.assertEqual(posixpath.expanduser(path), path)
@unittest.skipIf(sys.platform == "vxworks",
"no home directory on VxWorks")
def test_expanduser_pwd2(self):
pwd = import_helper.import_module('pwd')
getpwall = support.get_attribute(pwd, 'getpwall')
names = [entry.pw_name for entry in getpwall()]
maxusers = 1000 if support.is_resource_enabled('cpu') else 100
if len(names) > maxusers:
# Select random names, half of them with non-ASCII name,
# if available.
random.shuffle(names)
names.sort(key=lambda name: name.isascii())
del names[maxusers//2:-maxusers//2]
for name in names:
# gh-121200: pw_dir can be different between getpwall() and
# getpwnam(), so use getpwnam() pw_dir as expanduser() does.
entry = pwd.getpwnam(name)
home = entry.pw_dir
home = home.rstrip('/') or '/'
with self.subTest(name=name, pw_dir=entry.pw_dir):
self.assertEqual(posixpath.expanduser('~' + name), home)
self.assertEqual(posixpath.expanduser(os.fsencode('~' + name)),
os.fsencode(home))
NORMPATH_CASES = [
("", "."),
("/", "/"),
("/.", "/"),
("/./", "/"),
("/.//.", "/"),
("/./foo/bar", "/foo/bar"),
("/foo", "/foo"),
("/foo/bar", "/foo/bar"),
("//", "//"),
@@ -351,6 +402,7 @@ class PosixPathTest(unittest.TestCase):
("///..//./foo/.//bar", "/foo/bar"),
(".", "."),
(".//.", "."),
("./foo/bar", "foo/bar"),
("..", ".."),
("../", ".."),
("../foo", "../foo"),
@@ -400,32 +452,35 @@ class PosixPathTest(unittest.TestCase):
self.assertEqual(result, expected)
@skip_if_ABSTFN_contains_backslash
def test_realpath_curdir(self):
self.assertEqual(realpath('.'), os.getcwd())
self.assertEqual(realpath('./.'), os.getcwd())
self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd())
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_curdir(self, kwargs):
self.assertEqual(realpath('.', **kwargs), os.getcwd())
self.assertEqual(realpath('./.', **kwargs), os.getcwd())
self.assertEqual(realpath('/'.join(['.'] * 100), **kwargs), os.getcwd())
self.assertEqual(realpath(b'.'), os.getcwdb())
self.assertEqual(realpath(b'./.'), os.getcwdb())
self.assertEqual(realpath(b'/'.join([b'.'] * 100)), os.getcwdb())
self.assertEqual(realpath(b'.', **kwargs), os.getcwdb())
self.assertEqual(realpath(b'./.', **kwargs), os.getcwdb())
self.assertEqual(realpath(b'/'.join([b'.'] * 100), **kwargs), os.getcwdb())
@skip_if_ABSTFN_contains_backslash
def test_realpath_pardir(self):
self.assertEqual(realpath('..'), dirname(os.getcwd()))
self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd())))
self.assertEqual(realpath('/'.join(['..'] * 100)), '/')
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_pardir(self, kwargs):
self.assertEqual(realpath('..', **kwargs), dirname(os.getcwd()))
self.assertEqual(realpath('../..', **kwargs), dirname(dirname(os.getcwd())))
self.assertEqual(realpath('/'.join(['..'] * 100), **kwargs), '/')
self.assertEqual(realpath(b'..'), dirname(os.getcwdb()))
self.assertEqual(realpath(b'../..'), dirname(dirname(os.getcwdb())))
self.assertEqual(realpath(b'/'.join([b'..'] * 100)), b'/')
self.assertEqual(realpath(b'..', **kwargs), dirname(os.getcwdb()))
self.assertEqual(realpath(b'../..', **kwargs), dirname(dirname(os.getcwdb())))
self.assertEqual(realpath(b'/'.join([b'..'] * 100), **kwargs), b'/')
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_basic(self):
@_parameterize({}, {'strict': ALLOW_MISSING})
def test_realpath_basic(self, kwargs):
# Basic operation.
try:
os.symlink(ABSTFN+"1", ABSTFN)
self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
self.assertEqual(realpath(ABSTFN, **kwargs), ABSTFN+"1")
finally:
os_helper.unlink(ABSTFN)
@@ -441,15 +496,122 @@ class PosixPathTest(unittest.TestCase):
finally:
os_helper.unlink(ABSTFN)
def test_realpath_invalid_paths(self):
path = '/\x00'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(ValueError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = b'/\x00'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(ValueError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = '/nonexistent/x\x00'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = b'/nonexistent/x\x00'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = '/\x00/..'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(ValueError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = b'/\x00/..'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(ValueError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = '/nonexistent/x\x00/..'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = b'/nonexistent/x\x00/..'
self.assertRaises(ValueError, realpath, path, strict=False)
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING)
path = '/\udfff'
if sys.platform == 'win32':
self.assertEqual(realpath(path, strict=False), path)
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
self.assertEqual(realpath(path, strict=ALLOW_MISSING), path)
else:
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING)
path = '/nonexistent/\udfff'
if sys.platform == 'win32':
self.assertEqual(realpath(path, strict=False), path)
self.assertEqual(realpath(path, strict=ALLOW_MISSING), path)
else:
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING)
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
path = '/\udfff/..'
if sys.platform == 'win32':
self.assertEqual(realpath(path, strict=False), '/')
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
self.assertEqual(realpath(path, strict=ALLOW_MISSING), '/')
else:
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING)
path = '/nonexistent/\udfff/..'
if sys.platform == 'win32':
self.assertEqual(realpath(path, strict=False), '/nonexistent')
self.assertEqual(realpath(path, strict=ALLOW_MISSING), '/nonexistent')
else:
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING)
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
path = b'/\xff'
if sys.platform == 'win32':
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
self.assertRaises(UnicodeDecodeError, realpath, path, strict=ALLOW_MISSING)
else:
self.assertEqual(realpath(path, strict=False), path)
if support.is_wasi:
self.assertRaises(OSError, realpath, path, strict=True)
self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING)
else:
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
self.assertEqual(realpath(path, strict=ALLOW_MISSING), path)
path = b'/nonexistent/\xff'
if sys.platform == 'win32':
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
self.assertRaises(UnicodeDecodeError, realpath, path, strict=ALLOW_MISSING)
else:
self.assertEqual(realpath(path, strict=False), path)
if support.is_wasi:
self.assertRaises(OSError, realpath, path, strict=True)
self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING)
else:
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_relative(self):
@_parameterize({}, {'strict': ALLOW_MISSING})
def test_realpath_relative(self, kwargs):
try:
os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN)
self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
self.assertEqual(realpath(ABSTFN, **kwargs), ABSTFN+"1")
finally:
os_helper.unlink(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
@_parameterize({}, {'strict': ALLOW_MISSING})
def test_realpath_missing_pardir(self, kwargs):
try:
os.symlink(TESTFN + "1", TESTFN)
self.assertEqual(
realpath("nonexistent/../" + TESTFN, **kwargs), ABSTFN + "1")
finally:
os_helper.unlink(TESTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_symlink_loops(self):
@@ -469,7 +631,7 @@ class PosixPathTest(unittest.TestCase):
self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
os.symlink(ABSTFN+"x", ABSTFN+"y")
self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
ABSTFN + "y")
ABSTFN + "x")
self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
ABSTFN + "1")
@@ -493,37 +655,38 @@ class PosixPathTest(unittest.TestCase):
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_symlink_loops_strict(self):
@_parameterize({'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_symlink_loops_strict(self, kwargs):
# Bug #43757, raise OSError if we get into an infinite symlink loop in
# strict mode.
# the strict modes.
try:
os.symlink(ABSTFN, ABSTFN)
self.assertRaises(OSError, realpath, ABSTFN, strict=True)
self.assertRaises(OSError, realpath, ABSTFN, **kwargs)
os.symlink(ABSTFN+"1", ABSTFN+"2")
os.symlink(ABSTFN+"2", ABSTFN+"1")
self.assertRaises(OSError, realpath, ABSTFN+"1", strict=True)
self.assertRaises(OSError, realpath, ABSTFN+"2", strict=True)
self.assertRaises(OSError, realpath, ABSTFN+"1", **kwargs)
self.assertRaises(OSError, realpath, ABSTFN+"2", **kwargs)
self.assertRaises(OSError, realpath, ABSTFN+"1/x", strict=True)
self.assertRaises(OSError, realpath, ABSTFN+"1/..", strict=True)
self.assertRaises(OSError, realpath, ABSTFN+"1/../x", strict=True)
self.assertRaises(OSError, realpath, ABSTFN+"1/x", **kwargs)
self.assertRaises(OSError, realpath, ABSTFN+"1/..", **kwargs)
self.assertRaises(OSError, realpath, ABSTFN+"1/../x", **kwargs)
os.symlink(ABSTFN+"x", ABSTFN+"y")
self.assertRaises(OSError, realpath,
ABSTFN+"1/../" + basename(ABSTFN) + "y", strict=True)
ABSTFN+"1/../" + basename(ABSTFN) + "y", **kwargs)
self.assertRaises(OSError, realpath,
ABSTFN+"1/../" + basename(ABSTFN) + "1", strict=True)
ABSTFN+"1/../" + basename(ABSTFN) + "1", **kwargs)
os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
self.assertRaises(OSError, realpath, ABSTFN+"a", strict=True)
self.assertRaises(OSError, realpath, ABSTFN+"a", **kwargs)
os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
basename(ABSTFN) + "c", ABSTFN+"c")
self.assertRaises(OSError, realpath, ABSTFN+"c", strict=True)
self.assertRaises(OSError, realpath, ABSTFN+"c", **kwargs)
# Test using relative path as well.
with os_helper.change_cwd(dirname(ABSTFN)):
self.assertRaises(OSError, realpath, basename(ABSTFN), strict=True)
self.assertRaises(OSError, realpath, basename(ABSTFN), **kwargs)
finally:
os_helper.unlink(ABSTFN)
os_helper.unlink(ABSTFN+"1")
@@ -534,28 +697,30 @@ class PosixPathTest(unittest.TestCase):
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_repeated_indirect_symlinks(self):
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_repeated_indirect_symlinks(self, kwargs):
# Issue #6975.
try:
os.mkdir(ABSTFN)
os.symlink('../' + basename(ABSTFN), ABSTFN + '/self')
os.symlink('self/self/self', ABSTFN + '/link')
self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN)
self.assertEqual(realpath(ABSTFN + '/link', **kwargs), ABSTFN)
finally:
os_helper.unlink(ABSTFN + '/self')
os_helper.unlink(ABSTFN + '/link')
safe_rmdir(ABSTFN)
os_helper.rmdir(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_deep_recursion(self):
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_deep_recursion(self, kwargs):
depth = 10
try:
os.mkdir(ABSTFN)
for i in range(depth):
os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1))
os.symlink('.', ABSTFN + '/0')
self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
self.assertEqual(realpath(ABSTFN + '/%d' % depth, **kwargs), ABSTFN)
# Test using relative path as well.
with os_helper.change_cwd(ABSTFN):
@@ -563,11 +728,12 @@ class PosixPathTest(unittest.TestCase):
finally:
for i in range(depth + 1):
os_helper.unlink(ABSTFN + '/%d' % i)
safe_rmdir(ABSTFN)
os_helper.rmdir(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_parents(self):
@_parameterize({}, {'strict': ALLOW_MISSING})
def test_realpath_resolve_parents(self, kwargs):
# We also need to resolve any symlinks in the parents of a relative
# path passed to realpath. E.g.: current working directory is
# /usr/doc with 'doc' being a symlink to /usr/share/doc. We call
@@ -578,15 +744,17 @@ class PosixPathTest(unittest.TestCase):
os.symlink(ABSTFN + "/y", ABSTFN + "/k")
with os_helper.change_cwd(ABSTFN + "/k"):
self.assertEqual(realpath("a"), ABSTFN + "/y/a")
self.assertEqual(realpath("a", **kwargs),
ABSTFN + "/y/a")
finally:
os_helper.unlink(ABSTFN + "/k")
safe_rmdir(ABSTFN + "/y")
safe_rmdir(ABSTFN)
os_helper.rmdir(ABSTFN + "/y")
os_helper.rmdir(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_before_normalizing(self):
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_resolve_before_normalizing(self, kwargs):
# Bug #990669: Symbolic links should be resolved before we
# normalize the path. E.g.: if we have directories 'a', 'k' and 'y'
# in the following hierarchy:
@@ -601,20 +769,21 @@ class PosixPathTest(unittest.TestCase):
os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y")
# Absolute path.
self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k")
self.assertEqual(realpath(ABSTFN + "/link-y/..", **kwargs), ABSTFN + "/k")
# Relative path.
with os_helper.change_cwd(dirname(ABSTFN)):
self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
self.assertEqual(realpath(basename(ABSTFN) + "/link-y/..", **kwargs),
ABSTFN + "/k")
finally:
os_helper.unlink(ABSTFN + "/link-y")
safe_rmdir(ABSTFN + "/k/y")
safe_rmdir(ABSTFN + "/k")
safe_rmdir(ABSTFN)
os_helper.rmdir(ABSTFN + "/k/y")
os_helper.rmdir(ABSTFN + "/k")
os_helper.rmdir(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_first(self):
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_resolve_first(self, kwargs):
# Bug #1213894: The first component of the path, if not absolute,
# must be resolved too.
@@ -624,17 +793,192 @@ class PosixPathTest(unittest.TestCase):
os.symlink(ABSTFN, ABSTFN + "link")
with os_helper.change_cwd(dirname(ABSTFN)):
base = basename(ABSTFN)
self.assertEqual(realpath(base + "link"), ABSTFN)
self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
self.assertEqual(realpath(base + "link", **kwargs), ABSTFN)
self.assertEqual(realpath(base + "link/k", **kwargs), ABSTFN + "/k")
finally:
os_helper.unlink(ABSTFN + "link")
safe_rmdir(ABSTFN + "/k")
safe_rmdir(ABSTFN)
os_helper.rmdir(ABSTFN + "/k")
os_helper.rmdir(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
@unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set symlink permissions")
@unittest.skipIf(sys.platform != "darwin", "only macOS requires read permission to readlink()")
def test_realpath_unreadable_symlink(self):
try:
os.symlink(ABSTFN+"1", ABSTFN)
os.chmod(ABSTFN, 0o000, follow_symlinks=False)
self.assertEqual(realpath(ABSTFN), ABSTFN)
self.assertEqual(realpath(ABSTFN + '/foo'), ABSTFN + '/foo')
self.assertEqual(realpath(ABSTFN + '/../foo'), dirname(ABSTFN) + '/foo')
self.assertEqual(realpath(ABSTFN + '/foo/..'), ABSTFN)
finally:
os.chmod(ABSTFN, 0o755, follow_symlinks=False)
os_helper.unlink(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
@unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set symlink permissions")
@unittest.skipIf(sys.platform != "darwin", "only macOS requires read permission to readlink()")
@_parameterize({'strict': True}, {'strict': ALLOW_MISSING})
def test_realpath_unreadable_symlink_strict(self, kwargs):
try:
os.symlink(ABSTFN+"1", ABSTFN)
os.chmod(ABSTFN, 0o000, follow_symlinks=False)
with self.assertRaises(PermissionError):
realpath(ABSTFN, **kwargs)
with self.assertRaises(PermissionError):
realpath(ABSTFN + '/foo', **kwargs),
with self.assertRaises(PermissionError):
realpath(ABSTFN + '/../foo', **kwargs)
with self.assertRaises(PermissionError):
realpath(ABSTFN + '/foo/..', **kwargs)
finally:
os.chmod(ABSTFN, 0o755, follow_symlinks=False)
os.unlink(ABSTFN)
@skip_if_ABSTFN_contains_backslash
@os_helper.skip_unless_symlink
def test_realpath_unreadable_directory(self):
try:
os.mkdir(ABSTFN)
os.mkdir(ABSTFN + '/k')
os.chmod(ABSTFN, 0o000)
self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN)
self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN)
self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN)
try:
os.stat(ABSTFN)
except PermissionError:
pass
else:
self.skipTest('Cannot block permissions')
self.assertEqual(realpath(ABSTFN + '/k', strict=False),
ABSTFN + '/k')
self.assertRaises(PermissionError, realpath, ABSTFN + '/k',
strict=True)
self.assertRaises(PermissionError, realpath, ABSTFN + '/k',
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + '/missing', strict=False),
ABSTFN + '/missing')
self.assertRaises(PermissionError, realpath, ABSTFN + '/missing',
strict=True)
self.assertRaises(PermissionError, realpath, ABSTFN + '/missing',
strict=ALLOW_MISSING)
finally:
os.chmod(ABSTFN, 0o755)
os_helper.rmdir(ABSTFN + '/k')
os_helper.rmdir(ABSTFN)
@skip_if_ABSTFN_contains_backslash
def test_realpath_nonterminal_file(self):
try:
with open(ABSTFN, 'w') as f:
f.write('test_posixpath wuz ere')
self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN)
self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN)
self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN)
self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN))
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "/subdir")
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir",
strict=ALLOW_MISSING)
finally:
os_helper.unlink(ABSTFN)
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_nonterminal_symlink_to_file(self):
try:
with open(ABSTFN + "1", 'w') as f:
f.write('test_posixpath wuz ere')
os.symlink(ABSTFN + "1", ABSTFN)
self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "1")
self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "1")
self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN + "1")
self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "1")
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "1")
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN))
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "1/subdir")
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir",
strict=ALLOW_MISSING)
finally:
os_helper.unlink(ABSTFN)
os_helper.unlink(ABSTFN + "1")
@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_nonterminal_symlink_to_symlinks_to_file(self):
try:
with open(ABSTFN + "2", 'w') as f:
f.write('test_posixpath wuz ere')
os.symlink(ABSTFN + "2", ABSTFN + "1")
os.symlink(ABSTFN + "1", ABSTFN)
self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "2")
self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2")
self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2")
self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "2")
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "2")
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN))
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..",
strict=ALLOW_MISSING)
self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "2/subdir")
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True)
self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir",
strict=ALLOW_MISSING)
finally:
os_helper.unlink(ABSTFN)
os_helper.unlink(ABSTFN + "1")
os_helper.unlink(ABSTFN + "2")
def test_relpath(self):
(real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
try:
curdir = os.path.split(os.getcwd())[-1]
self.assertRaises(TypeError, posixpath.relpath, None)
self.assertRaises(ValueError, posixpath.relpath, "")
self.assertEqual(posixpath.relpath("a"), "a")
self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a")
@@ -697,7 +1041,9 @@ class PosixPathTest(unittest.TestCase):
self.assertRaises(exc, posixpath.commonpath,
[os.fsencode(p) for p in paths])
self.assertRaises(TypeError, posixpath.commonpath, None)
self.assertRaises(ValueError, posixpath.commonpath, [])
self.assertRaises(ValueError, posixpath.commonpath, iter([]))
check_error(ValueError, ['/usr', 'usr'])
check_error(ValueError, ['usr', '/usr'])
@@ -742,62 +1088,22 @@ class PosixPathTest(unittest.TestCase):
['usr/lib/', b'/usr/lib/python3'])
# TODO: RUSTPYTHON
@unittest.skip("TODO: RUSTPYTHON, flaky tests")
class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase):
pathmodule = posixpath
attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
# TODO: RUSTPYTHON
if os.name == "posix" and os.getenv("CI"):
@unittest.expectedFailure
def test_exists(self):
super().test_exists()
# TODO: RUSTPYTHON
import sys
@unittest.skipIf(sys.platform.startswith("linux") and os.getenv("CI"), "TODO: RUSTPYTHON, flaky test")
def test_filetime(self):
super().test_filetime()
# TODO: RUSTPYTHON
if sys.platform.startswith("linux"):
@unittest.expectedFailure
def test_nonascii_abspath(self):
super().test_nonascii_abspath()
# TODO: RUSTPYTHON
if os.name == "nt":
@unittest.expectedFailure
def test_samefile(self):
super().test_samefile()
# TODO: RUSTPYTHON
if os.name == "nt":
@unittest.expectedFailure
def test_samefile_on_link(self):
super().test_samefile_on_link()
# TODO: RUSTPYTHON
if os.name == "nt":
@unittest.expectedFailure
def test_samestat(self):
super().test_samestat()
# TODO: RUSTPYTHON
if os.name == "nt":
@unittest.expectedFailure
def test_samestat_on_link(self):
super().test_samestat_on_link()
# TODO: RUSTPYTHON
@unittest.skipIf(os.getenv("CI"), "TODO: RUSTPYTHON, FileExistsError: (17, 'File exists (os error 17)')")
class PathLikeTests(unittest.TestCase):
path = posixpath
def setUp(self):
self.file_name = os_helper.TESTFN
self.file_path = FakePath(os_helper.TESTFN)
self.file_name = TESTFN
self.file_path = FakePath(TESTFN)
self.addCleanup(os_helper.unlink, self.file_name)
with open(self.file_name, 'xb', 0) as file:
file.write(b"test_posixpath.PathLikeTests")
@@ -854,9 +1160,12 @@ class PathLikeTests(unittest.TestCase):
def test_path_abspath(self):
self.assertPathEqual(self.path.abspath)
def test_path_realpath(self):
@_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
def test_path_realpath(self, kwargs):
self.assertPathEqual(self.path.realpath)
self.assertPathEqual(partial(self.path.realpath, **kwargs))
def test_path_relpath(self):
self.assertPathEqual(self.path.relpath)