From 6285774f06f44f04353801cc79fd2a5e67f884ec Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Mon, 6 Sep 2010 17:07:27 +0000 Subject: [PATCH] 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. --- Doc/library/os.path.rst | 4 +++- Lib/ntpath.py | 7 ++++++- Lib/test/test_ntpath.py | 13 +++++++++++++ Misc/NEWS | 2 ++ Modules/posixmodule.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 317f55aa95a..7aac5c444d7 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -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. - Availability: Unix. + Availability: Unix, Windows. + + .. versionchanged:: 3.2 Added Windows support. .. function:: samestat(stat1, stat2) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index ee7ac679c03..eae3cf3098a 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -10,6 +10,7 @@ import sys import stat import genericpath from genericpath import * +from nt import _getfileinformation __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -17,7 +18,7 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "ismount", "expanduser","expandvars","normpath","abspath", "splitunc","curdir","pardir","sep","pathsep","defpath","altsep", "extsep","devnull","realpath","supports_unicode_filenames","relpath", - "samefile",] + "samefile", "sameopenfile",] # strings representing various path-related bits and pieces # 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 # Windows-XP approximation. return abspath(f1) == abspath(f2) + +def sameopenfile(f1, f2): + """Test whether two file objects reference the same file""" + return _getfileinformation(f1) == _getfileinformation(f2) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 86e3eda46b8..9f39abaec33 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -2,6 +2,7 @@ import ntpath import os from test.support import TestFailed from test import support, test_genericpath +from tempfile import TemporaryFile import unittest @@ -237,6 +238,18 @@ class TestNtpath(unittest.TestCase): tester('ntpath.relpath("/a", "/a")', '.') 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): pathmodule = ntpath diff --git a/Misc/NEWS b/Misc/NEWS index 732c1455894..1ac1dec3f49 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Core and Builtins Library ------- +- Issue #7566: Implement os.path.sameopenfile for Windows. + - Issue #9293: I/O streams now raise ``io.UnsupportedOperation`` when an unsupported operation is attempted (for example, writing to a file open only for reading). diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index dda758f4457..a83a06bb31f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2758,6 +2758,33 @@ posix__getfinalpathname(PyObject *self, PyObject *args) return result; } /* 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 */ PyDoc_STRVAR(posix_mkdir__doc__, @@ -7908,6 +7935,7 @@ static PyMethodDef posix_methods[] = { #ifdef MS_WINDOWS {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL}, {"_getfinalpathname", posix__getfinalpathname, METH_VARARGS, NULL}, + {"_getfileinformation", posix__getfileinformation, METH_VARARGS, NULL}, #endif #ifdef HAVE_GETLOADAVG {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},