Implement #7566 - os.path.sameopenfile for Windows.

This uses the GetFileInformationByHandle function to return a tuple of values
to identify a file, then ntpath.sameopenfile compares file tuples, which
is exposed as os.path.sameopenfile.
This commit is contained in:
Brian Curtin 2010-09-06 17:07:27 +00:00
parent 5c997b8d90
commit 6285774f06
5 changed files with 52 additions and 2 deletions

View File

@ -251,7 +251,9 @@ applications should use string objects to access all files.
Return ``True`` if the file descriptors *fp1* and *fp2* refer to the same file. Return ``True`` if the file descriptors *fp1* and *fp2* refer to the same file.
Availability: Unix. Availability: Unix, Windows.
.. versionchanged:: 3.2 Added Windows support.
.. function:: samestat(stat1, stat2) .. function:: samestat(stat1, stat2)

View File

@ -10,6 +10,7 @@ import sys
import stat import stat
import genericpath import genericpath
from genericpath import * from genericpath import *
from nt import _getfileinformation
__all__ = ["normcase","isabs","join","splitdrive","split","splitext", __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
"basename","dirname","commonprefix","getsize","getmtime", "basename","dirname","commonprefix","getsize","getmtime",
@ -17,7 +18,7 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
"ismount", "expanduser","expandvars","normpath","abspath", "ismount", "expanduser","expandvars","normpath","abspath",
"splitunc","curdir","pardir","sep","pathsep","defpath","altsep", "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
"extsep","devnull","realpath","supports_unicode_filenames","relpath", "extsep","devnull","realpath","supports_unicode_filenames","relpath",
"samefile",] "samefile", "sameopenfile",]
# strings representing various path-related bits and pieces # strings representing various path-related bits and pieces
# These are primarily for export; internally, they are hardcoded. # These are primarily for export; internally, they are hardcoded.
@ -652,3 +653,7 @@ def samefile(f1, f2):
# Also, on other operating systems, fake this method with a # Also, on other operating systems, fake this method with a
# Windows-XP approximation. # Windows-XP approximation.
return abspath(f1) == abspath(f2) return abspath(f1) == abspath(f2)
def sameopenfile(f1, f2):
"""Test whether two file objects reference the same file"""
return _getfileinformation(f1) == _getfileinformation(f2)

View File

@ -2,6 +2,7 @@ import ntpath
import os import os
from test.support import TestFailed from test.support import TestFailed
from test import support, test_genericpath from test import support, test_genericpath
from tempfile import TemporaryFile
import unittest import unittest
@ -237,6 +238,18 @@ class TestNtpath(unittest.TestCase):
tester('ntpath.relpath("/a", "/a")', '.') tester('ntpath.relpath("/a", "/a")', '.')
tester('ntpath.relpath("/a/b", "/a/b")', '.') tester('ntpath.relpath("/a/b", "/a/b")', '.')
def test_sameopenfile(self):
with TemporaryFile() as tf1, TemporaryFile() as tf2:
# Make sure the same file is really the same
self.assertTrue(ntpath.sameopenfile(tf1.fileno(), tf1.fileno()))
# Make sure different files are really different
self.assertFalse(ntpath.sameopenfile(tf1.fileno(), tf2.fileno()))
# Make sure invalid values don't cause issues
with self.assertRaises(ValueError):
# Invalid file descriptors shouldn't display assert
# dialogs (#4804)
ntpath.sameopenfile(-1, -1)
class NtCommonTest(test_genericpath.CommonTest): class NtCommonTest(test_genericpath.CommonTest):
pathmodule = ntpath pathmodule = ntpath

View File

@ -13,6 +13,8 @@ Core and Builtins
Library Library
------- -------
- Issue #7566: Implement os.path.sameopenfile for Windows.
- Issue #9293: I/O streams now raise ``io.UnsupportedOperation`` when an - Issue #9293: I/O streams now raise ``io.UnsupportedOperation`` when an
unsupported operation is attempted (for example, writing to a file open unsupported operation is attempted (for example, writing to a file open
only for reading). only for reading).

View File

@ -2758,6 +2758,33 @@ posix__getfinalpathname(PyObject *self, PyObject *args)
return result; return result;
} /* end of posix__getfinalpathname */ } /* 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)) {
PyErr_SetString(PyExc_ValueError, "received invalid file descriptor");
return NULL;
}
hFile = (HANDLE)_get_osfhandle(fd);
if (hFile == INVALID_HANDLE_VALUE)
return win32_error("_getfileinformation", NULL);
if (!GetFileInformationByHandle(hFile, &info))
return win32_error("_getfileinformation", NULL);
return Py_BuildValue("iii", info.dwVolumeSerialNumber,
info.nFileIndexHigh,
info.nFileIndexLow);
}
#endif /* MS_WINDOWS */ #endif /* MS_WINDOWS */
PyDoc_STRVAR(posix_mkdir__doc__, PyDoc_STRVAR(posix_mkdir__doc__,
@ -7908,6 +7935,7 @@ static PyMethodDef posix_methods[] = {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
{"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL}, {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL},
{"_getfinalpathname", posix__getfinalpathname, METH_VARARGS, NULL}, {"_getfinalpathname", posix__getfinalpathname, METH_VARARGS, NULL},
{"_getfileinformation", posix__getfileinformation, METH_VARARGS, NULL},
#endif #endif
#ifdef HAVE_GETLOADAVG #ifdef HAVE_GETLOADAVG
{"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__}, {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},