Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd33aa18

Browse files
authored
gh-82367: UseFindFirstFile Win32 API inntpath.realpath() (GH-110298)
* Use `FindFirstFile` Win32 API to fix a bug where `ntpath.realpath()`breaks out of traversing a series of paths where a (handled)`ERROR_ACCESS_DENIED` or `ERROR_SHARING_VIOLATION` occurs.* Update docs to reflect that `ntpath.realpath()` eliminates MS-DOSstyle names.
1 parent2cb62c6 commitd33aa18

File tree

8 files changed

+132
-8
lines changed

8 files changed

+132
-8
lines changed

‎Doc/library/os.path.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,8 @@ the :mod:`glob` module.)
377377

378378
Return the canonical path of the specified filename, eliminating any symbolic
379379
links encountered in the path (if they are supported by the operating
380-
system).
380+
system). On Windows, this function will also resolve MS-DOS (also called 8.3)
381+
style names such as ``C:\\PROGRA~1`` to ``C:\\Program Files``.
381382

382383
If a path doesn't exist or a symlink loop is encountered, and *strict* is
383384
``True``,:exc:`OSError` is raised. If *strict* is ``False``, the path is

‎Doc/whatsnew/3.13.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ Other Language Changes
9797
if supported.
9898
(Contributed by Victor Stinner in:gh:`109649`.)
9999

100+
*:func:`os.path.realpath` now resolves MS-DOS style file names even if
101+
the file is not accessible.
102+
(Contributed by Moonsik Park in:gh:`82367`.)
100103

101104
New Modules
102105
===========

‎Lib/ntpath.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
importgenericpath
2424
fromgenericpathimport*
2525

26-
2726
__all__= ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
2827
"basename","dirname","commonprefix","getsize","getmtime",
2928
"getatime","getctime","islink","exists","lexists","isdir","isfile",
@@ -601,7 +600,7 @@ def abspath(path):
601600
return_abspath_fallback(path)
602601

603602
try:
604-
fromntimport_getfinalpathname,readlinkas_nt_readlink
603+
fromntimport_findfirstfile,_getfinalpathname,readlinkas_nt_readlink
605604
exceptImportError:
606605
# realpath is a no-op on systems without _getfinalpathname support.
607606
realpath=abspath
@@ -688,10 +687,15 @@ def _getfinalpathname_nonstrict(path):
688687
exceptOSError:
689688
# If we fail to readlink(), let's keep traversing
690689
pass
691-
path,name=split(path)
692-
# TODO (bpo-38186): Request the real file name from the directory
693-
# entry using FindFirstFileW. For now, we will return the path
694-
# as best we have it
690+
# If we get these errors, try to get the real name of the file without accessing it.
691+
ifex.winerrorin (1,5,32,50,87,1920,1921):
692+
try:
693+
name=_findfirstfile(path)
694+
path,_=split(path)
695+
exceptOSError:
696+
path,name=split(path)
697+
else:
698+
path,name=split(path)
695699
ifpathandnotname:
696700
returnpath+tail
697701
tail=join(name,tail)iftailelsename

‎Lib/test/test_ntpath.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
importntpath
33
importos
44
importstring
5+
importsubprocess
56
importsys
67
importunittest
78
importwarnings
@@ -637,6 +638,48 @@ def test_realpath_cwd(self):
637638
withos_helper.change_cwd(test_dir_short):
638639
self.assertPathEqual(test_file_long,ntpath.realpath("file.txt"))
639640

641+
@unittest.skipUnless(HAVE_GETFINALPATHNAME,'need _getfinalpathname')
642+
deftest_realpath_permission(self):
643+
# Test whether python can resolve the real filename of a
644+
# shortened file name even if it does not have permission to access it.
645+
ABSTFN=ntpath.realpath(os_helper.TESTFN)
646+
647+
os_helper.unlink(ABSTFN)
648+
os_helper.rmtree(ABSTFN)
649+
os.mkdir(ABSTFN)
650+
self.addCleanup(os_helper.rmtree,ABSTFN)
651+
652+
test_file=ntpath.join(ABSTFN,"LongFileName123.txt")
653+
test_file_short=ntpath.join(ABSTFN,"LONGFI~1.TXT")
654+
655+
withopen(test_file,"wb")asf:
656+
f.write(b"content")
657+
# Automatic generation of short names may be disabled on
658+
# NTFS volumes for the sake of performance.
659+
# They're not supported at all on ReFS and exFAT.
660+
subprocess.run(
661+
# Try to set the short name manually.
662+
['fsutil.exe','file','setShortName',test_file,'LONGFI~1.TXT'],
663+
creationflags=subprocess.DETACHED_PROCESS
664+
)
665+
666+
try:
667+
self.assertPathEqual(test_file,ntpath.realpath(test_file_short))
668+
exceptAssertionError:
669+
raiseunittest.SkipTest('the filesystem seems to lack support for short filenames')
670+
671+
# Deny the right to [S]YNCHRONIZE on the file to
672+
# force nt._getfinalpathname to fail with ERROR_ACCESS_DENIED.
673+
p=subprocess.run(
674+
['icacls.exe',test_file,'/deny','*S-1-5-32-545:(S)'],
675+
creationflags=subprocess.DETACHED_PROCESS
676+
)
677+
678+
ifp.returncode:
679+
raiseunittest.SkipTest('failed to deny access to the test file')
680+
681+
self.assertPathEqual(test_file,ntpath.realpath(test_file_short))
682+
640683
deftest_expandvars(self):
641684
withos_helper.EnvironmentVarGuard()asenv:
642685
env.clear()

‎Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,7 @@ Peter Parente
13731373
Alexandre Parenteau
13741374
Dan Parisien
13751375
HyeSoo Park
1376+
Moonsik Park
13761377
William Park
13771378
Claude Paroz
13781379
Heikki Partanen
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`os.path.realpath` now resolves MS-DOS style file names even if
2+
the file is not accessible. Patch by Moonsik Park.

‎Modules/clinic/posixmodule.c.h

Lines changed: 39 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Modules/posixmodule.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4809,6 +4809,37 @@ os__getfinalpathname_impl(PyObject *module, path_t *path)
48094809
returnresult;
48104810
}
48114811

4812+
/*[clinic input]
4813+
os._findfirstfile
4814+
path: path_t
4815+
/
4816+
A function to get the real file name without accessing the file in Windows.
4817+
[clinic start generated code]*/
4818+
4819+
staticPyObject*
4820+
os__findfirstfile_impl(PyObject*module,path_t*path)
4821+
/*[clinic end generated code: output=106dd3f0779c83dd input=0734dff70f60e1a8]*/
4822+
{
4823+
PyObject*result;
4824+
HANDLEhFindFile;
4825+
WIN32_FIND_DATAWwFileData;
4826+
WCHAR*wRealFileName;
4827+
4828+
Py_BEGIN_ALLOW_THREADS
4829+
hFindFile=FindFirstFileW(path->wide,&wFileData);
4830+
Py_END_ALLOW_THREADS
4831+
4832+
if (hFindFile==INVALID_HANDLE_VALUE) {
4833+
path_error(path);
4834+
returnNULL;
4835+
}
4836+
4837+
wRealFileName=wFileData.cFileName;
4838+
result=PyUnicode_FromWideChar(wRealFileName,wcslen(wRealFileName));
4839+
FindClose(hFindFile);
4840+
returnresult;
4841+
}
4842+
48124843

48134844
/*[clinic input]
48144845
os._getvolumepathname
@@ -15961,6 +15992,7 @@ static PyMethodDef posix_methods[] = {
1596115992
OS__GETFULLPATHNAME_METHODDEF
1596215993
OS__GETDISKUSAGE_METHODDEF
1596315994
OS__GETFINALPATHNAME_METHODDEF
15995+
OS__FINDFIRSTFILE_METHODDEF
1596415996
OS__GETVOLUMEPATHNAME_METHODDEF
1596515997
OS__PATH_SPLITROOT_METHODDEF
1596615998
OS__PATH_NORMPATH_METHODDEF

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp