[Python-checkins] cpython: Issue #9035: os.path.ismount now recognises volumes mounted below

tim.golden python-checkins at python.org
Thu Aug 1 13:45:55 CEST 2013


http://hg.python.org/cpython/rev/f283589cb71e
changeset:   84945:f283589cb71e
user:        Tim Golden <mail at timgolden.me.uk>
date:        Thu Aug 01 12:44:00 2013 +0100
summary:
  Issue #9035: os.path.ismount now recognises volumes mounted below
a drive root on Windows. Original patch by Atsuo Ishimoto.

files:
  Lib/ntpath.py           |  29 ++++++++++++++++---
  Lib/test/test_ntpath.py |  34 +++++++++++++++++++++++
  Misc/NEWS               |   3 ++
  Modules/posixmodule.c   |  42 +++++++++++++++++++++++++++++
  4 files changed, 103 insertions(+), 5 deletions(-)


diff --git a/Lib/ntpath.py b/Lib/ntpath.py
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -335,16 +335,35 @@
         return False
     return True
 
-# Is a path a mount point?  Either a root (with or without drive letter)
-# or an UNC path with at most a / or \ after the mount point.
-
+# Is a path a mount point?
+# Any drive letter root (eg c:\)
+# Any share UNC (eg \\server\share)
+# Any volume mounted on a filesystem folder
+#
+# No one method detects all three situations. Historically we've lexically
+# detected drive letter roots and share UNCs. The canonical approach to
+# detecting mounted volumes (querying the reparse tag) fails for the most
+# common case: drive letter roots. The alternative which uses GetVolumePathName
+# fails if the drive letter is the result of a SUBST.
+try:
+    from nt import _getvolumepathname
+except ImportError:
+    _getvolumepathname = None
 def ismount(path):
-    """Test whether a path is a mount point (defined as root of drive)"""
+    """Test whether a path is a mount point (a drive root, the root of a
+    share, or a mounted volume)"""
     seps = _get_bothseps(path)
+    path = abspath(path)
     root, rest = splitdrive(path)
     if root and root[0] in seps:
         return (not rest) or (rest in seps)
-    return rest in seps
+    if rest in seps:
+        return True
+
+    if _getvolumepathname:
+        return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps)
+    else:
+        return False
 
 
 # Expand paths beginning with '~' or '~user'.
diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -256,6 +256,40 @@
                     # dialogs (#4804)
                     ntpath.sameopenfile(-1, -1)
 
+    def test_ismount(self):
+        self.assertTrue(ntpath.ismount("c:\\"))
+        self.assertTrue(ntpath.ismount("C:\\"))
+        self.assertTrue(ntpath.ismount("c:/"))
+        self.assertTrue(ntpath.ismount("C:/"))
+        self.assertTrue(ntpath.ismount("\\\\.\\c:\\"))
+        self.assertTrue(ntpath.ismount("\\\\.\\C:\\"))
+
+        self.assertTrue(ntpath.ismount(b"c:\\"))
+        self.assertTrue(ntpath.ismount(b"C:\\"))
+        self.assertTrue(ntpath.ismount(b"c:/"))
+        self.assertTrue(ntpath.ismount(b"C:/"))
+        self.assertTrue(ntpath.ismount(b"\\\\.\\c:\\"))
+        self.assertTrue(ntpath.ismount(b"\\\\.\\C:\\"))
+
+        with support.temp_dir() as d:
+            self.assertFalse(ntpath.ismount(d))
+
+        #
+        # Make sure the current folder isn't the root folder
+        # (or any other volume root). The drive-relative
+        # locations below cannot then refer to mount points
+        #
+        drive, path = ntpath.splitdrive(sys.executable)
+        with support.change_cwd(os.path.dirname(sys.executable)):
+            self.assertFalse(ntpath.ismount(drive.lower()))
+            self.assertFalse(ntpath.ismount(drive.upper()))
+
+        self.assertTrue(ntpath.ismount("\\\\localhost\\c$"))
+        self.assertTrue(ntpath.ismount("\\\\localhost\\c$\\"))
+
+        self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
+        self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
+
 
 class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
     pathmodule = ntpath
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
 Core and Builtins
 -----------------
 
+- Issue #9035: ismount now recognises volumes mounted below a drive root
+  on Windows. Original patch by Atsuo Ishimoto.
+
 - Issue #18214: Improve finalization of Python modules to avoid setting
   their globals to None, in most cases.
 
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3711,6 +3711,47 @@
     else
         Py_RETURN_FALSE;
 }
+
+PyDoc_STRVAR(posix__getvolumepathname__doc__,
+"Return volume mount point of the specified path.");
+
+/* A helper function for ismount on windows */
+static PyObject *
+posix__getvolumepathname(PyObject *self, PyObject *args)
+{
+    PyObject *po, *result;
+    wchar_t *path, *mountpath=NULL;
+    size_t bufsize;
+    BOOL ret;
+
+    if (!PyArg_ParseTuple(args, "U|:_getvolumepathname", &po))
+        return NULL;
+    path = PyUnicode_AsUnicode(po);
+    if (path == NULL)
+        return NULL;
+
+    /* Volume path should be shorter than entire path */
+    bufsize = max(MAX_PATH, wcslen(path) * 2 * sizeof(wchar_t)+1);
+    mountpath = (wchar_t *)PyMem_Malloc(bufsize);
+    if (mountpath == NULL)
+        return PyErr_NoMemory();
+
+    Py_BEGIN_ALLOW_THREADS
+    ret = GetVolumePathNameW(path, mountpath, bufsize);
+    Py_END_ALLOW_THREADS
+
+    if (!ret) {
+        result = win32_error_object("_getvolumepathname", po);
+        goto exit;
+    }
+    result = PyUnicode_FromWideChar(mountpath, wcslen(mountpath));
+
+exit:
+    PyMem_Free(mountpath);
+    return result;
+}
+/* end of posix__getvolumepathname */
+
 #endif /* MS_WINDOWS */
 
 PyDoc_STRVAR(posix_mkdir__doc__,
@@ -10885,6 +10926,7 @@
     {"_getfinalpathname",       posix__getfinalpathname, METH_VARARGS, NULL},
     {"_isdir",                  posix__isdir, METH_VARARGS, posix__isdir__doc__},
     {"_getdiskusage",           win32__getdiskusage, METH_VARARGS, win32__getdiskusage__doc__},
+    {"_getvolumepathname",      posix__getvolumepathname, METH_VARARGS, posix__getvolumepathname__doc__},
 #endif
 #ifdef HAVE_GETLOADAVG
     {"getloadavg",      posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},

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


More information about the Python-checkins mailing list