summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/ntpath.py29
-rw-r--r--Lib/test/test_ntpath.py34
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/posixmodule.c42
4 files changed, 103 insertions, 5 deletions
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
index d81f728..6b65697 100644
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -335,16 +335,35 @@ def lexists(path):
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
index f809876..66fcf27 100644
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -256,6 +256,40 @@ class TestNtpath(unittest.TestCase):
# 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
index df075b0..088ad42 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1?
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
index 820983c..06cf1df 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3711,6 +3711,47 @@ check:
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 @@ static PyMethodDef posix_methods[] = {
{"_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__},