Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Implement ALL_BUT_LAST on Windows.
  • Loading branch information
serhiy-storchaka committed May 19, 2025
commit b76a1c5f349ef56190e54ba97801914ee46d3b71
11 changes: 10 additions & 1 deletion Lib/ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,9 @@ def abspath(path):
path = join(getcwd(), path)
return normpath(path)

# A singleton with true boolean value
ALL_BUT_LAST = ['ALL_BUT_LAST']

try:
from nt import _findfirstfile, _getfinalpathname, readlink as _nt_readlink
except ImportError:
Expand Down Expand Up @@ -735,7 +738,13 @@ def realpath(path, *, strict=False):
path = normpath(path)
except OSError as ex:
if strict:
raise
if strict is not ALL_BUT_LAST or not isinstance(ex, FileNotFoundError):
raise
dirname, basename = split(path)
if not basename:
dirname, basename = split(path)
if not isdir(dirname):
raise
initial_winerror = ex.winerror
path = _getfinalpathname_nonstrict(path)
# The path returned by _getfinalpathname will always start with \\?\ -
Expand Down
127 changes: 127 additions & 0 deletions Lib/test/test_ntpath.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import errno
import inspect
import ntpath
import os
Expand Down Expand Up @@ -705,6 +706,132 @@ def test_realpath_permission(self):

self.assertPathEqual(test_file, ntpath.realpath(test_file_short))

@os_helper.skip_unless_symlink
def test_realpath_mode(self):
realpath = ntpath.realpath
ALL_BUT_LAST = ntpath.ALL_BUT_LAST
ABSTFN = ntpath.abspath(os_helper.TESTFN)
try:
os.mkdir(ABSTFN)
os.mkdir(ABSTFN + "\\dir")
open(ABSTFN + "\\file", "wb").close()
open(ABSTFN + "\\dir\\file2", "wb").close()
os.symlink("file", ABSTFN + "\\link")
os.symlink("dir", ABSTFN + "\\link2")
os.symlink("nonexistent", ABSTFN + "\\broken")
os.symlink("cycle", ABSTFN + "\\cycle")
def check(path, mode, expected, errno=None):
path = path.replace('/', '\\')
if isinstance(expected, str):
assert errno is None
self.assertEqual(realpath(path, strict=mode), ABSTFN + expected.replace('/', '\\'))
else:
with self.assertRaises(expected) as cm:
realpath(path, strict=mode)
if errno is not None:
self.assertEqual(cm.exception.errno, errno)

with os_helper.change_cwd(ABSTFN):
check("file", False, "/file")
check("file", ALL_BUT_LAST, "/file")
check("file", True, "/file")
check("file/", False, "/file")
# check("file/", ALL_BUT_LAST, NotADirectoryError)
# check("file/", True, NotADirectoryError)
check("file/file2", False, "/file/file2")
# check("file/file2", ALL_BUT_LAST, NotADirectoryError)
# check("file/file2", True, NotADirectoryError)
check("file/.", False, "/file")
# check("file/.", ALL_BUT_LAST, NotADirectoryError)
# check("file/.", True, NotADirectoryError)
check("file/../link2", False, "/dir")
check("file/../link2", ALL_BUT_LAST, "/dir")
check("file/../link2", True, "/dir")

check("dir", False, "/dir")
check("dir", ALL_BUT_LAST, "/dir")
check("dir", True, "/dir")
check("dir/", False, "/dir")
check("dir/", ALL_BUT_LAST, "/dir")
check("dir/", True, "/dir")
check("dir/file2", False, "/dir/file2")
check("dir/file2", ALL_BUT_LAST, "/dir/file2")
check("dir/file2", True, "/dir/file2")

check("link", False, "/file")
check("link", ALL_BUT_LAST, "/file")
check("link", True, "/file")
check("link/", False, "/file")
# check("link/", ALL_BUT_LAST, NotADirectoryError)
# check("link/", True, NotADirectoryError)
check("link/file2", False, "/file/file2")
# check("link/file2", ALL_BUT_LAST, NotADirectoryError)
# check("link/file2", True, NotADirectoryError)
check("link/.", False, "/file")
# check("link/.", ALL_BUT_LAST, NotADirectoryError)
# check("link/.", True, NotADirectoryError)
check("link/../link", False, "/file")
check("link/../link", ALL_BUT_LAST, "/file")
check("link/../link", True, "/file")

check("link2", False, "/dir")
check("link2", ALL_BUT_LAST, "/dir")
check("link2", True, "/dir")
check("link2/", False, "/dir")
check("link2/", ALL_BUT_LAST, "/dir")
check("link2/", True, "/dir")
check("link2/file2", False, "/dir/file2")
check("link2/file2", ALL_BUT_LAST, "/dir/file2")
check("link2/file2", True, "/dir/file2")

check("nonexistent", False, "/nonexistent")
check("nonexistent", ALL_BUT_LAST, "/nonexistent")
check("nonexistent", True, FileNotFoundError)
check("nonexistent/", False, "/nonexistent")
check("nonexistent/", ALL_BUT_LAST, "/nonexistent")
check("nonexistent/", True, FileNotFoundError)
check("nonexistent/file", False, "/nonexistent/file")
check("nonexistent/file", ALL_BUT_LAST, FileNotFoundError)
check("nonexistent/file", True, FileNotFoundError)
check("nonexistent/../link", False, "/file")
check("nonexistent/../link", ALL_BUT_LAST, "/file")
check("nonexistent/../link", True, "/file")

check("broken", False, "/nonexistent")
check("broken", ALL_BUT_LAST, "/nonexistent")
check("broken", True, FileNotFoundError)
check("broken/", False, "/nonexistent")
check("broken/", ALL_BUT_LAST, "/nonexistent")
check("broken/", True, FileNotFoundError)
check("broken/file", False, "/nonexistent/file")
check("broken/file", ALL_BUT_LAST, FileNotFoundError)
check("broken/file", True, FileNotFoundError)
check("broken/../link", False, "/file")
check("broken/../link", ALL_BUT_LAST, "/file")
check("broken/../link", True, "/file")

check("cycle", False, "/cycle")
check("cycle", ALL_BUT_LAST, OSError, errno.EINVAL)
check("cycle", True, OSError, errno.EINVAL)
check("cycle/", False, "/cycle")
check("cycle/", ALL_BUT_LAST, OSError, errno.EINVAL)
check("cycle/", True, OSError, errno.EINVAL)
check("cycle/file", False, "/cycle/file")
check("cycle/file", ALL_BUT_LAST, OSError, errno.EINVAL)
check("cycle/file", True, OSError, errno.EINVAL)
check("cycle/../link", False, "/file")
check("cycle/../link", ALL_BUT_LAST, "/file")
check("cycle/../link", True, "/file")
finally:
os_helper.unlink(ABSTFN + "/file")
os_helper.unlink(ABSTFN + "/dir/file2")
os_helper.unlink(ABSTFN + "/link")
os_helper.unlink(ABSTFN + "/link2")
os_helper.unlink(ABSTFN + "/broken")
os_helper.unlink(ABSTFN + "/cycle")
os_helper.rmdir(ABSTFN + "/dir")
os_helper.rmdir(ABSTFN)

def test_expandvars(self):
with os_helper.EnvironmentVarGuard() as env:
env.clear()
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ def test_realpath_nonterminal_symlink_to_symlinks_to_file(self):
os_helper.unlink(ABSTFN)

@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
# @skip_if_ABSTFN_contains_backslash
def test_realpath_mode(self):
try:
os.mkdir(ABSTFN)
Expand All @@ -781,7 +781,8 @@ def test_realpath_mode(self):
def check(path, mode, expected, errno=None):
if isinstance(expected, str):
assert errno is None
self.assertEqual(realpath(path, strict=mode), ABSTFN + expected)
self.assertEqual(realpath(path, strict=mode).replace('/', os.sep),
ABSTFN.replace('/', os.sep) + expected.replace('/', os.sep))
else:
with self.assertRaises(expected) as cm:
realpath(path, strict=mode)
Expand Down
Loading