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

Skip to content

Commit 490b32a

Browse files
committed
Fix #11939. Set st_dev attribute on Windows to simplify os.path.samefile.
By setting the st_dev attribute, we can then remove some Windows-specific code and move os.path.samefile/sameopenfile/samestat to Lib/genericpath.py so all platforms share the same implementation.
1 parent 2bf61ab commit 490b32a

7 files changed

Lines changed: 104 additions & 139 deletions

File tree

Doc/library/os.path.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,14 @@ applications should use string objects to access all files.
244244
On Unix, this is determined by the device number and i-node number and raises an
245245
exception if a :func:`os.stat` call on either pathname fails.
246246

247-
On Windows, two files are the same if they resolve to the same final path
248-
name using the Windows API call GetFinalPathNameByHandle. This function
249-
raises an exception if handles cannot be obtained to either file.
250-
251247
Availability: Unix, Windows.
252248

253249
.. versionchanged:: 3.2
254250
Added Windows support.
255251

252+
.. versionchanged:: 3.4
253+
Windows now uses the same implementation as all other platforms.
254+
256255

257256
.. function:: sameopenfile(fp1, fp2)
258257

@@ -271,7 +270,10 @@ applications should use string objects to access all files.
271270
:func:`stat`. This function implements the underlying comparison used by
272271
:func:`samefile` and :func:`sameopenfile`.
273272

274-
Availability: Unix.
273+
Availability: Unix, Windows.
274+
275+
.. versionchanged:: 3.4
276+
Added Windows support.
275277

276278

277279
.. function:: split(path)

Lib/genericpath.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import stat
88

99
__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
10-
'getsize', 'isdir', 'isfile']
10+
'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile',
11+
'samestat']
1112

1213

1314
# Does a path exist?
@@ -75,6 +76,31 @@ def commonprefix(m):
7576
return s1[:i]
7677
return s1
7778

79+
# Are two stat buffers (obtained from stat, fstat or lstat)
80+
# describing the same file?
81+
def samestat(s1, s2):
82+
"""Test whether two stat buffers reference the same file"""
83+
return (s1.st_ino == s2.st_ino and
84+
s1.st_dev == s2.st_dev)
85+
86+
87+
# Are two filenames really pointing to the same file?
88+
def samefile(f1, f2):
89+
"""Test whether two pathnames reference the same actual file"""
90+
s1 = os.stat(f1)
91+
s2 = os.stat(f2)
92+
return samestat(s1, s2)
93+
94+
95+
# Are two open files really referencing the same file?
96+
# (Not necessarily the same file descriptor!)
97+
def sameopenfile(fp1, fp2):
98+
"""Test whether two open file objects reference the same file"""
99+
s1 = os.fstat(fp1)
100+
s2 = os.fstat(fp2)
101+
return samestat(s1, s2)
102+
103+
78104
# Split a path in root and extension.
79105
# The extension is everything starting at the last dot in the last
80106
# pathname component; the root is everything before that.

Lib/ntpath.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -652,23 +652,6 @@ def relpath(path, start=curdir):
652652
def _getfinalpathname(f):
653653
return normcase(abspath(f))
654654

655-
def samefile(f1, f2):
656-
"Test whether two pathnames reference the same actual file"
657-
return _getfinalpathname(f1) == _getfinalpathname(f2)
658-
659-
660-
try:
661-
from nt import _getfileinformation
662-
except ImportError:
663-
# On other operating systems, just return the fd and see that
664-
# it compares equal in sameopenfile.
665-
def _getfileinformation(fd):
666-
return fd
667-
668-
def sameopenfile(f1, f2):
669-
"""Test whether two file objects reference the same file"""
670-
return _getfileinformation(f1) == _getfileinformation(f2)
671-
672655

673656
try:
674657
# The genericpath.isdir implementation uses os.stat and checks the mode

Lib/posixpath.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -177,34 +177,6 @@ def lexists(path):
177177
return True
178178

179179

180-
# Are two filenames really pointing to the same file?
181-
182-
def samefile(f1, f2):
183-
"""Test whether two pathnames reference the same actual file"""
184-
s1 = os.stat(f1)
185-
s2 = os.stat(f2)
186-
return samestat(s1, s2)
187-
188-
189-
# Are two open files really referencing the same file?
190-
# (Not necessarily the same file descriptor!)
191-
192-
def sameopenfile(fp1, fp2):
193-
"""Test whether two open file objects reference the same file"""
194-
s1 = os.fstat(fp1)
195-
s2 = os.fstat(fp2)
196-
return samestat(s1, s2)
197-
198-
199-
# Are two stat buffers (obtained from stat, fstat or lstat)
200-
# describing the same file?
201-
202-
def samestat(s1, s2):
203-
"""Test whether two stat buffers reference the same file"""
204-
return s1.st_ino == s2.st_ino and \
205-
s1.st_dev == s2.st_dev
206-
207-
208180
# Is a path a mount point?
209181
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
210182

Lib/test/test_genericpath.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,74 @@ def test_isfile(self):
190190
support.unlink(support.TESTFN)
191191
safe_rmdir(support.TESTFN)
192192

193+
@staticmethod
194+
def _create_file(filename):
195+
with open(filename, 'wb') as f:
196+
f.write(b'foo')
197+
198+
def test_samefile(self):
199+
try:
200+
test_fn = support.TESTFN + "1"
201+
self._create_file(test_fn)
202+
self.assertTrue(self.pathmodule.samefile(test_fn, test_fn))
203+
self.assertRaises(TypeError, self.pathmodule.samefile)
204+
finally:
205+
os.remove(test_fn)
206+
207+
@support.skip_unless_symlink
208+
def test_samefile_on_links(self):
209+
try:
210+
test_fn1 = support.TESTFN + "1"
211+
test_fn2 = support.TESTFN + "2"
212+
self._create_file(test_fn1)
213+
214+
os.symlink(test_fn1, test_fn2)
215+
self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2))
216+
os.remove(test_fn2)
217+
218+
self._create_file(test_fn2)
219+
self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2))
220+
finally:
221+
os.remove(test_fn1)
222+
os.remove(test_fn2)
223+
224+
def test_samestat(self):
225+
try:
226+
test_fn = support.TESTFN + "1"
227+
self._create_file(test_fn)
228+
test_fns = [test_fn]*2
229+
stats = map(os.stat, test_fns)
230+
self.assertTrue(self.pathmodule.samestat(*stats))
231+
finally:
232+
os.remove(test_fn)
233+
234+
@support.skip_unless_symlink
235+
def test_samestat_on_links(self):
236+
try:
237+
test_fn1 = support.TESTFN + "1"
238+
test_fn2 = support.TESTFN + "2"
239+
self._create_file(test_fn1)
240+
test_fns = (test_fn1, test_fn2)
241+
os.symlink(*test_fns)
242+
stats = map(os.stat, test_fns)
243+
self.assertTrue(self.pathmodule.samestat(*stats))
244+
os.remove(test_fn2)
245+
246+
self._create_file(test_fn2)
247+
stats = map(os.stat, test_fns)
248+
self.assertFalse(self.pathmodule.samestat(*stats))
249+
250+
self.assertRaises(TypeError, self.pathmodule.samestat)
251+
finally:
252+
os.remove(test_fn1)
253+
os.remove(test_fn2)
254+
255+
def test_sameopenfile(self):
256+
fname = support.TESTFN + "1"
257+
with open(fname, "wb") as a, open(fname, "wb") as b:
258+
self.assertTrue(self.pathmodule.sameopenfile(
259+
a.fileno(), b.fileno()))
260+
193261

194262
# Following TestCase is not supposed to be run from test_genericpath.
195263
# It is inherited by other test modules (macpath, ntpath, posixpath).

Lib/test/test_posixpath.py

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -186,63 +186,6 @@ def test_islink(self):
186186
if not f.close():
187187
f.close()
188188

189-
@staticmethod
190-
def _create_file(filename):
191-
with open(filename, 'wb') as f:
192-
f.write(b'foo')
193-
194-
def test_samefile(self):
195-
test_fn = support.TESTFN + "1"
196-
self._create_file(test_fn)
197-
self.assertTrue(posixpath.samefile(test_fn, test_fn))
198-
self.assertRaises(TypeError, posixpath.samefile)
199-
200-
@unittest.skipIf(
201-
sys.platform.startswith('win'),
202-
"posixpath.samefile does not work on links in Windows")
203-
@unittest.skipUnless(hasattr(os, "symlink"),
204-
"Missing symlink implementation")
205-
def test_samefile_on_links(self):
206-
test_fn1 = support.TESTFN + "1"
207-
test_fn2 = support.TESTFN + "2"
208-
self._create_file(test_fn1)
209-
210-
os.symlink(test_fn1, test_fn2)
211-
self.assertTrue(posixpath.samefile(test_fn1, test_fn2))
212-
os.remove(test_fn2)
213-
214-
self._create_file(test_fn2)
215-
self.assertFalse(posixpath.samefile(test_fn1, test_fn2))
216-
217-
218-
def test_samestat(self):
219-
test_fn = support.TESTFN + "1"
220-
self._create_file(test_fn)
221-
test_fns = [test_fn]*2
222-
stats = map(os.stat, test_fns)
223-
self.assertTrue(posixpath.samestat(*stats))
224-
225-
@unittest.skipIf(
226-
sys.platform.startswith('win'),
227-
"posixpath.samestat does not work on links in Windows")
228-
@unittest.skipUnless(hasattr(os, "symlink"),
229-
"Missing symlink implementation")
230-
def test_samestat_on_links(self):
231-
test_fn1 = support.TESTFN + "1"
232-
test_fn2 = support.TESTFN + "2"
233-
self._create_file(test_fn1)
234-
test_fns = (test_fn1, test_fn2)
235-
os.symlink(*test_fns)
236-
stats = map(os.stat, test_fns)
237-
self.assertTrue(posixpath.samestat(*stats))
238-
os.remove(test_fn2)
239-
240-
self._create_file(test_fn2)
241-
stats = map(os.stat, test_fns)
242-
self.assertFalse(posixpath.samestat(*stats))
243-
244-
self.assertRaises(TypeError, posixpath.samestat)
245-
246189
def test_ismount(self):
247190
self.assertIs(posixpath.ismount("/"), True)
248191
with warnings.catch_warnings():
@@ -518,11 +461,6 @@ def test_relpath_bytes(self):
518461
finally:
519462
os.getcwdb = real_getcwdb
520463

521-
def test_sameopenfile(self):
522-
fname = support.TESTFN + "1"
523-
with open(fname, "wb") as a, open(fname, "wb") as b:
524-
self.assertTrue(posixpath.sameopenfile(a.fileno(), b.fileno()))
525-
526464

527465
class PosixCommonTest(test_genericpath.CommonTest):
528466
pathmodule = posixpath

Modules/posixmodule.c

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,8 @@ attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag, stru
12491249
memset(result, 0, sizeof(*result));
12501250
result->st_mode = attributes_to_mode(info->dwFileAttributes);
12511251
result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
1252+
result->st_dev = info->dwVolumeSerialNumber;
1253+
result->st_rdev = result->st_dev;
12521254
FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec);
12531255
FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
12541256
FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
@@ -3503,31 +3505,6 @@ posix__getfinalpathname(PyObject *self, PyObject *args)
35033505

35043506
} /* end of posix__getfinalpathname */
35053507

3506-
static PyObject *
3507-
posix__getfileinformation(PyObject *self, PyObject *args)
3508-
{
3509-
HANDLE hFile;
3510-
BY_HANDLE_FILE_INFORMATION info;
3511-
int fd;
3512-
3513-
if (!PyArg_ParseTuple(args, "i:_getfileinformation", &fd))
3514-
return NULL;
3515-
3516-
if (!_PyVerify_fd(fd))
3517-
return posix_error();
3518-
3519-
hFile = (HANDLE)_get_osfhandle(fd);
3520-
if (hFile == INVALID_HANDLE_VALUE)
3521-
return posix_error();
3522-
3523-
if (!GetFileInformationByHandle(hFile, &info))
3524-
return PyErr_SetFromWindowsErr(0);
3525-
3526-
return Py_BuildValue("iii", info.dwVolumeSerialNumber,
3527-
info.nFileIndexHigh,
3528-
info.nFileIndexLow);
3529-
}
3530-
35313508
PyDoc_STRVAR(posix__isdir__doc__,
35323509
"Return true if the pathname refers to an existing directory.");
35333510

@@ -10606,7 +10583,6 @@ static PyMethodDef posix_methods[] = {
1060610583
#ifdef MS_WINDOWS
1060710584
{"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL},
1060810585
{"_getfinalpathname", posix__getfinalpathname, METH_VARARGS, NULL},
10609-
{"_getfileinformation", posix__getfileinformation, METH_VARARGS, NULL},
1061010586
{"_isdir", posix__isdir, METH_VARARGS, posix__isdir__doc__},
1061110587
{"_getdiskusage", win32__getdiskusage, METH_VARARGS, win32__getdiskusage__doc__},
1061210588
#endif

0 commit comments

Comments
 (0)