[Python-checkins] cpython: Fix #11939. Set st_dev attribute on Windows to simplify os.path.samefile.

brian.curtin python-checkins at python.org
Wed Dec 26 14:03:38 CET 2012


http://hg.python.org/cpython/rev/189b21f9bc0c
changeset:   81062:189b21f9bc0c
user:        Brian Curtin <brian at python.org>
date:        Wed Dec 26 07:03:03 2012 -0600
summary:
  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.

files:
  Doc/library/os.path.rst      |  12 ++-
  Lib/genericpath.py           |  28 +++++++++-
  Lib/ntpath.py                |  17 ------
  Lib/posixpath.py             |  28 ---------
  Lib/test/test_genericpath.py |  68 ++++++++++++++++++++++++
  Lib/test/test_posixpath.py   |  62 ---------------------
  Modules/posixmodule.c        |  28 +---------
  7 files changed, 104 insertions(+), 139 deletions(-)


diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst
--- a/Doc/library/os.path.rst
+++ b/Doc/library/os.path.rst
@@ -244,15 +244,14 @@
    On Unix, this is determined by the device number and i-node number and raises an
    exception if a :func:`os.stat` call on either pathname fails.
 
-   On Windows, two files are the same if they resolve to the same final path
-   name using the Windows API call GetFinalPathNameByHandle. This function
-   raises an exception if handles cannot be obtained to either file.
-
    Availability: Unix, Windows.
 
    .. versionchanged:: 3.2
       Added Windows support.
 
+   .. versionchanged:: 3.4
+      Windows now uses the same implementation as all other platforms.
+
 
 .. function:: sameopenfile(fp1, fp2)
 
@@ -271,7 +270,10 @@
    :func:`stat`.  This function implements the underlying comparison used by
    :func:`samefile` and :func:`sameopenfile`.
 
-   Availability: Unix.
+   Availability: Unix, Windows.
+
+   .. versionchanged:: 3.4
+      Added Windows support.
 
 
 .. function:: split(path)
diff --git a/Lib/genericpath.py b/Lib/genericpath.py
--- a/Lib/genericpath.py
+++ b/Lib/genericpath.py
@@ -7,7 +7,8 @@
 import stat
 
 __all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
-           'getsize', 'isdir', 'isfile']
+           'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile',
+           'samestat']
 
 
 # Does a path exist?
@@ -75,6 +76,31 @@
             return s1[:i]
     return s1
 
+# Are two stat buffers (obtained from stat, fstat or lstat)
+# describing the same file?
+def samestat(s1, s2):
+    """Test whether two stat buffers reference the same file"""
+    return (s1.st_ino == s2.st_ino and
+            s1.st_dev == s2.st_dev)
+
+
+# Are two filenames really pointing to the same file?
+def samefile(f1, f2):
+    """Test whether two pathnames reference the same actual file"""
+    s1 = os.stat(f1)
+    s2 = os.stat(f2)
+    return samestat(s1, s2)
+
+
+# Are two open files really referencing the same file?
+# (Not necessarily the same file descriptor!)
+def sameopenfile(fp1, fp2):
+    """Test whether two open file objects reference the same file"""
+    s1 = os.fstat(fp1)
+    s2 = os.fstat(fp2)
+    return samestat(s1, s2)
+
+
 # Split a path in root and extension.
 # The extension is everything starting at the last dot in the last
 # pathname component; the root is everything before that.
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -652,23 +652,6 @@
     def _getfinalpathname(f):
         return normcase(abspath(f))
 
-def samefile(f1, f2):
-    "Test whether two pathnames reference the same actual file"
-    return _getfinalpathname(f1) == _getfinalpathname(f2)
-
-
-try:
-    from nt import _getfileinformation
-except ImportError:
-    # On other operating systems, just return the fd and see that
-    # it compares equal in sameopenfile.
-    def _getfileinformation(fd):
-        return fd
-
-def sameopenfile(f1, f2):
-    """Test whether two file objects reference the same file"""
-    return _getfileinformation(f1) == _getfileinformation(f2)
-
 
 try:
     # The genericpath.isdir implementation uses os.stat and checks the mode
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
--- a/Lib/posixpath.py
+++ b/Lib/posixpath.py
@@ -177,34 +177,6 @@
     return True
 
 
-# Are two filenames really pointing to the same file?
-
-def samefile(f1, f2):
-    """Test whether two pathnames reference the same actual file"""
-    s1 = os.stat(f1)
-    s2 = os.stat(f2)
-    return samestat(s1, s2)
-
-
-# Are two open files really referencing the same file?
-# (Not necessarily the same file descriptor!)
-
-def sameopenfile(fp1, fp2):
-    """Test whether two open file objects reference the same file"""
-    s1 = os.fstat(fp1)
-    s2 = os.fstat(fp2)
-    return samestat(s1, s2)
-
-
-# Are two stat buffers (obtained from stat, fstat or lstat)
-# describing the same file?
-
-def samestat(s1, s2):
-    """Test whether two stat buffers reference the same file"""
-    return s1.st_ino == s2.st_ino and \
-           s1.st_dev == s2.st_dev
-
-
 # Is a path a mount point?
 # (Does this work for all UNIXes?  Is it even guaranteed to work by Posix?)
 
diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py
--- a/Lib/test/test_genericpath.py
+++ b/Lib/test/test_genericpath.py
@@ -190,6 +190,74 @@
             support.unlink(support.TESTFN)
             safe_rmdir(support.TESTFN)
 
+    @staticmethod
+    def _create_file(filename):
+        with open(filename, 'wb') as f:
+            f.write(b'foo')
+
+    def test_samefile(self):
+        try:
+            test_fn = support.TESTFN + "1"
+            self._create_file(test_fn)
+            self.assertTrue(self.pathmodule.samefile(test_fn, test_fn))
+            self.assertRaises(TypeError, self.pathmodule.samefile)
+        finally:
+            os.remove(test_fn)
+
+    @support.skip_unless_symlink
+    def test_samefile_on_links(self):
+        try:
+            test_fn1 = support.TESTFN + "1"
+            test_fn2 = support.TESTFN + "2"
+            self._create_file(test_fn1)
+
+            os.symlink(test_fn1, test_fn2)
+            self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2))
+            os.remove(test_fn2)
+
+            self._create_file(test_fn2)
+            self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2))
+        finally:
+            os.remove(test_fn1)
+            os.remove(test_fn2)
+
+    def test_samestat(self):
+        try:
+            test_fn = support.TESTFN + "1"
+            self._create_file(test_fn)
+            test_fns = [test_fn]*2
+            stats = map(os.stat, test_fns)
+            self.assertTrue(self.pathmodule.samestat(*stats))
+        finally:
+            os.remove(test_fn)
+
+    @support.skip_unless_symlink
+    def test_samestat_on_links(self):
+        try:
+            test_fn1 = support.TESTFN + "1"
+            test_fn2 = support.TESTFN + "2"
+            self._create_file(test_fn1)
+            test_fns = (test_fn1, test_fn2)
+            os.symlink(*test_fns)
+            stats = map(os.stat, test_fns)
+            self.assertTrue(self.pathmodule.samestat(*stats))
+            os.remove(test_fn2)
+
+            self._create_file(test_fn2)
+            stats = map(os.stat, test_fns)
+            self.assertFalse(self.pathmodule.samestat(*stats))
+
+            self.assertRaises(TypeError, self.pathmodule.samestat)
+        finally:
+            os.remove(test_fn1)
+            os.remove(test_fn2)
+
+    def test_sameopenfile(self):
+        fname = support.TESTFN + "1"
+        with open(fname, "wb") as a, open(fname, "wb") as b:
+            self.assertTrue(self.pathmodule.sameopenfile(
+                                a.fileno(), b.fileno()))
+
 
 # Following TestCase is not supposed to be run from test_genericpath.
 # It is inherited by other test modules (macpath, ntpath, posixpath).
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -186,63 +186,6 @@
             if not f.close():
                 f.close()
 
-    @staticmethod
-    def _create_file(filename):
-        with open(filename, 'wb') as f:
-            f.write(b'foo')
-
-    def test_samefile(self):
-        test_fn = support.TESTFN + "1"
-        self._create_file(test_fn)
-        self.assertTrue(posixpath.samefile(test_fn, test_fn))
-        self.assertRaises(TypeError, posixpath.samefile)
-
-    @unittest.skipIf(
-        sys.platform.startswith('win'),
-        "posixpath.samefile does not work on links in Windows")
-    @unittest.skipUnless(hasattr(os, "symlink"),
-                         "Missing symlink implementation")
-    def test_samefile_on_links(self):
-        test_fn1 = support.TESTFN + "1"
-        test_fn2 = support.TESTFN + "2"
-        self._create_file(test_fn1)
-
-        os.symlink(test_fn1, test_fn2)
-        self.assertTrue(posixpath.samefile(test_fn1, test_fn2))
-        os.remove(test_fn2)
-
-        self._create_file(test_fn2)
-        self.assertFalse(posixpath.samefile(test_fn1, test_fn2))
-
-
-    def test_samestat(self):
-        test_fn = support.TESTFN + "1"
-        self._create_file(test_fn)
-        test_fns = [test_fn]*2
-        stats = map(os.stat, test_fns)
-        self.assertTrue(posixpath.samestat(*stats))
-
-    @unittest.skipIf(
-        sys.platform.startswith('win'),
-        "posixpath.samestat does not work on links in Windows")
-    @unittest.skipUnless(hasattr(os, "symlink"),
-                         "Missing symlink implementation")
-    def test_samestat_on_links(self):
-        test_fn1 = support.TESTFN + "1"
-        test_fn2 = support.TESTFN + "2"
-        self._create_file(test_fn1)
-        test_fns = (test_fn1, test_fn2)
-        os.symlink(*test_fns)
-        stats = map(os.stat, test_fns)
-        self.assertTrue(posixpath.samestat(*stats))
-        os.remove(test_fn2)
-
-        self._create_file(test_fn2)
-        stats = map(os.stat, test_fns)
-        self.assertFalse(posixpath.samestat(*stats))
-
-        self.assertRaises(TypeError, posixpath.samestat)
-
     def test_ismount(self):
         self.assertIs(posixpath.ismount("/"), True)
         with warnings.catch_warnings():
@@ -518,11 +461,6 @@
         finally:
             os.getcwdb = real_getcwdb
 
-    def test_sameopenfile(self):
-        fname = support.TESTFN + "1"
-        with open(fname, "wb") as a, open(fname, "wb") as b:
-            self.assertTrue(posixpath.sameopenfile(a.fileno(), b.fileno()))
-
 
 class PosixCommonTest(test_genericpath.CommonTest):
     pathmodule = posixpath
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1249,6 +1249,8 @@
     memset(result, 0, sizeof(*result));
     result->st_mode = attributes_to_mode(info->dwFileAttributes);
     result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
+    result->st_dev = info->dwVolumeSerialNumber;
+    result->st_rdev = result->st_dev;
     FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec);
     FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
     FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
@@ -3503,31 +3505,6 @@
 
 } /* end of posix__getfinalpathname */
 
-static PyObject *
-posix__getfileinformation(PyObject *self, PyObject *args)
-{
-    HANDLE hFile;
-    BY_HANDLE_FILE_INFORMATION info;
-    int fd;
-
-    if (!PyArg_ParseTuple(args, "i:_getfileinformation", &fd))
-        return NULL;
-
-    if (!_PyVerify_fd(fd))
-        return posix_error();
-
-    hFile = (HANDLE)_get_osfhandle(fd);
-    if (hFile == INVALID_HANDLE_VALUE)
-        return posix_error();
-
-    if (!GetFileInformationByHandle(hFile, &info))
-        return PyErr_SetFromWindowsErr(0);
-
-    return Py_BuildValue("iii", info.dwVolumeSerialNumber,
-                                info.nFileIndexHigh,
-                                info.nFileIndexLow);
-}
-
 PyDoc_STRVAR(posix__isdir__doc__,
 "Return true if the pathname refers to an existing directory.");
 
@@ -10606,7 +10583,6 @@
 #ifdef MS_WINDOWS
     {"_getfullpathname",        posix__getfullpathname, METH_VARARGS, NULL},
     {"_getfinalpathname",       posix__getfinalpathname, METH_VARARGS, NULL},
-    {"_getfileinformation",     posix__getfileinformation, METH_VARARGS, NULL},
     {"_isdir",                  posix__isdir, METH_VARARGS, posix__isdir__doc__},
     {"_getdiskusage",           win32__getdiskusage, METH_VARARGS, win32__getdiskusage__doc__},
 #endif

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list