[Python-checkins] cpython: Issue #21741: Add st_file_attributes to os.stat_result on Windows.

zach.ware python-checkins at python.org
Thu Jun 19 16:58:01 CEST 2014


http://hg.python.org/cpython/rev/706fab0213db
changeset:   91274:706fab0213db
user:        Zachary Ware <zachary.ware at gmail.com>
date:        Thu Jun 19 09:46:37 2014 -0500
summary:
  Issue #21741: Add st_file_attributes to os.stat_result on Windows.

Patch by Ben Hoyt.

files:
  Doc/library/os.rst    |   8 ++++++
  Doc/library/stat.rst  |  28 +++++++++++++++++++++++-
  Doc/whatsnew/3.5.rst  |   9 +++++++
  Lib/stat.py           |  23 +++++++++++++++++++
  Lib/test/test_os.py   |  22 ++++++++++++++++++
  Lib/test/test_stat.py |  29 ++++++++++++++++++++++++
  Misc/ACKS             |   1 +
  Misc/NEWS             |   3 ++
  Modules/_stat.c       |  36 +++++++++++++++++++++++++++++++
  Modules/posixmodule.c |  16 +++++++++++++
  10 files changed, 174 insertions(+), 1 deletions(-)


diff --git a/Doc/library/os.rst b/Doc/library/os.rst
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -1905,6 +1905,11 @@
    * :attr:`st_creator`
    * :attr:`st_type`
 
+   On Windows systems, the following attribute is also available:
+
+   * :attr:`st_file_attributes` - Windows file attribute bits (see the
+     ``FILE_ATTRIBUTE_*`` constants in the :mod:`stat` module)
+
    .. note::
 
       The exact meaning and resolution of the :attr:`st_atime`,
@@ -1958,6 +1963,9 @@
       and the :attr:`st_atime_ns`, :attr:`st_mtime_ns`,
       and :attr:`st_ctime_ns` members.
 
+   .. versionadded:: 3.5
+      Added the :attr:`st_file_attributes` member on Windows.
+
 
 .. function:: stat_float_times([newvalue])
 
diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst
--- a/Doc/library/stat.rst
+++ b/Doc/library/stat.rst
@@ -126,7 +126,7 @@
    if __name__ == '__main__':
        walktree(sys.argv[1], visitfile)
 
-An additional utility function is provided to covert a file's mode in a human
+An additional utility function is provided to convert a file's mode in a human
 readable string:
 
 .. function:: filemode(mode)
@@ -399,3 +399,29 @@
    The file is a snapshot file.
 
 See the \*BSD or Mac OS systems man page :manpage:`chflags(2)` for more information.
+
+On Windows, the following file attribute constants are available for use when
+testing bits in the ``st_file_attributes`` member returned by :func:`os.stat`.
+See the `Windows API documentation
+<http://msdn.microsoft.com/en-us/library/windows/desktop/gg258117.aspx>`_
+for more detail on the meaning of these constants.
+
+.. data:: FILE_ATTRIBUTE_ARCHIVE
+          FILE_ATTRIBUTE_COMPRESSED
+          FILE_ATTRIBUTE_DEVICE
+          FILE_ATTRIBUTE_DIRECTORY
+          FILE_ATTRIBUTE_ENCRYPTED
+          FILE_ATTRIBUTE_HIDDEN
+          FILE_ATTRIBUTE_INTEGRITY_STREAM
+          FILE_ATTRIBUTE_NORMAL
+          FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
+          FILE_ATTRIBUTE_NO_SCRUB_DATA
+          FILE_ATTRIBUTE_OFFLINE
+          FILE_ATTRIBUTE_READONLY
+          FILE_ATTRIBUTE_REPARSE_POINT
+          FILE_ATTRIBUTE_SPARSE_FILE
+          FILE_ATTRIBUTE_SYSTEM
+          FILE_ATTRIBUTE_TEMPORARY
+          FILE_ATTRIBUTE_VIRTUAL
+
+   .. versionadded:: 3.5
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -176,6 +176,15 @@
   network objects from existing addresses (contributed by Peter Moody
   and Antoine Pitrou in :issue:`16531`).
 
+os
+--
+
+* :class:`os.stat_result` now has a ``st_file_attributes`` field on Windows,
+  containing the ``dwFileAttributes`` member of the
+  ``BY_HANDLE_FILE_INFORMATION`` structure returned by
+  ``GetFileInformationByHandle()`` (contributed by Ben Hoyt in
+  :issue:`21719`).
+
 shutil
 ------
 
diff --git a/Lib/stat.py b/Lib/stat.py
--- a/Lib/stat.py
+++ b/Lib/stat.py
@@ -148,6 +148,29 @@
             perm.append("-")
     return "".join(perm)
 
+
+# Windows FILE_ATTRIBUTE constants for interpreting os.stat()'s
+# "st_file_attributes" member
+
+FILE_ATTRIBUTE_ARCHIVE = 32
+FILE_ATTRIBUTE_COMPRESSED = 2048
+FILE_ATTRIBUTE_DEVICE = 64
+FILE_ATTRIBUTE_DIRECTORY = 16
+FILE_ATTRIBUTE_ENCRYPTED = 16384
+FILE_ATTRIBUTE_HIDDEN = 2
+FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768
+FILE_ATTRIBUTE_NORMAL = 128
+FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192
+FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072
+FILE_ATTRIBUTE_OFFLINE = 4096
+FILE_ATTRIBUTE_READONLY = 1
+FILE_ATTRIBUTE_REPARSE_POINT = 1024
+FILE_ATTRIBUTE_SPARSE_FILE = 512
+FILE_ATTRIBUTE_SYSTEM = 4
+FILE_ATTRIBUTE_TEMPORARY = 256
+FILE_ATTRIBUTE_VIRTUAL = 65536
+
+
 # If available, use C implementation
 try:
     from _stat import *
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -530,6 +530,28 @@
             os.stat(r)
         self.assertEqual(ctx.exception.errno, errno.EBADF)
 
+    def check_file_attributes(self, result):
+        self.assertTrue(hasattr(result, 'st_file_attributes'))
+        self.assertTrue(isinstance(result.st_file_attributes, int))
+        self.assertTrue(0 <= result.st_file_attributes <= 0xFFFFFFFF)
+
+    @unittest.skipUnless(sys.platform == "win32",
+                         "st_file_attributes is Win32 specific")
+    def test_file_attributes(self):
+        # test file st_file_attributes (FILE_ATTRIBUTE_DIRECTORY not set)
+        result = os.stat(self.fname)
+        self.check_file_attributes(result)
+        self.assertEqual(
+            result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY,
+            0)
+
+        # test directory st_file_attributes (FILE_ATTRIBUTE_DIRECTORY set)
+        result = os.stat(support.TESTFN)
+        self.check_file_attributes(result)
+        self.assertEqual(
+            result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY,
+            stat.FILE_ATTRIBUTE_DIRECTORY)
+
 from test import mapping_tests
 
 class EnvironTests(mapping_tests.BasicTestMappingProtocol):
diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py
--- a/Lib/test/test_stat.py
+++ b/Lib/test/test_stat.py
@@ -1,5 +1,6 @@
 import unittest
 import os
+import sys
 from test.support import TESTFN, import_fresh_module
 
 c_stat = import_fresh_module('stat', fresh=['_stat'])
@@ -52,6 +53,26 @@
         'S_IWOTH': 0o002,
         'S_IXOTH': 0o001}
 
+    # defined by the Windows API documentation
+    file_attributes = {
+        'FILE_ATTRIBUTE_ARCHIVE': 32,
+        'FILE_ATTRIBUTE_COMPRESSED': 2048,
+        'FILE_ATTRIBUTE_DEVICE': 64,
+        'FILE_ATTRIBUTE_DIRECTORY': 16,
+        'FILE_ATTRIBUTE_ENCRYPTED': 16384,
+        'FILE_ATTRIBUTE_HIDDEN': 2,
+        'FILE_ATTRIBUTE_INTEGRITY_STREAM': 32768,
+        'FILE_ATTRIBUTE_NORMAL': 128,
+        'FILE_ATTRIBUTE_NOT_CONTENT_INDEXED': 8192,
+        'FILE_ATTRIBUTE_NO_SCRUB_DATA': 131072,
+        'FILE_ATTRIBUTE_OFFLINE': 4096,
+        'FILE_ATTRIBUTE_READONLY': 1,
+        'FILE_ATTRIBUTE_REPARSE_POINT': 1024,
+        'FILE_ATTRIBUTE_SPARSE_FILE': 512,
+        'FILE_ATTRIBUTE_SYSTEM': 4,
+        'FILE_ATTRIBUTE_TEMPORARY': 256,
+        'FILE_ATTRIBUTE_VIRTUAL': 65536}
+
     def setUp(self):
         try:
             os.remove(TESTFN)
@@ -185,6 +206,14 @@
             self.assertTrue(callable(func))
             self.assertEqual(func(0), 0)
 
+    @unittest.skipUnless(sys.platform == "win32",
+                         "FILE_ATTRIBUTE_* constants are Win32 specific")
+    def test_file_attribute_constants(self):
+        for key, value in sorted(self.file_attributes.items()):
+            self.assertTrue(hasattr(self.statmod, key), key)
+            modvalue = getattr(self.statmod, key)
+            self.assertEqual(value, modvalue, key)
+
 
 class TestFilemodeCStat(TestFilemode, unittest.TestCase):
     statmod = c_stat
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -580,6 +580,7 @@
 Ken Howard
 Brad Howes
 Mike Hoy
+Ben Hoyt
 Chih-Hao Huang
 Christian Hudon
 Lawrence Hudson
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -103,6 +103,9 @@
 Library
 -------
 
+- Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on
+  Windows.
+
 - Issue #21722: The distutils "upload" command now exits with a non-zero
   return code when uploading fails.  Patch by Martin Dengler.
 
diff --git a/Modules/_stat.c b/Modules/_stat.c
--- a/Modules/_stat.c
+++ b/Modules/_stat.c
@@ -27,9 +27,21 @@
 #endif /* HAVE_SYS_STAT_H */
 
 #ifdef MS_WINDOWS
+#include <windows.h>
 typedef unsigned short mode_t;
+
+/* FILE_ATTRIBUTE_INTEGRITY_STREAM and FILE_ATTRIBUTE_NO_SCRUB_DATA
+   are not present in VC2010, so define them manually */
+#ifndef FILE_ATTRIBUTE_INTEGRITY_STREAM
+#  define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x8000
 #endif
 
+#ifndef FILE_ATTRIBUTE_NO_SCRUB_DATA
+#  define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x20000
+#endif
+
+#endif /* MS_WINDOWS */
+
 /* From Python's stat.py */
 #ifndef S_IMODE
 #  define S_IMODE 07777
@@ -473,6 +485,10 @@
 ST_ATIME\n\
 ST_MTIME\n\
 ST_CTIME\n\
+\n"
+
+"FILE_ATTRIBUTE_*: Windows file attribute constants\n\
+                   (only present on Windows)\n\
 ");
 
 
@@ -555,6 +571,26 @@
     if (PyModule_AddIntConstant(m, "ST_MTIME", 8)) return NULL;
     if (PyModule_AddIntConstant(m, "ST_CTIME", 9)) return NULL;
 
+#ifdef MS_WINDOWS
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_ARCHIVE)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_COMPRESSED)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_DEVICE)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_DIRECTORY)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_ENCRYPTED)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_HIDDEN)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_INTEGRITY_STREAM)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NORMAL)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_NO_SCRUB_DATA)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_OFFLINE)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_READONLY)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_REPARSE_POINT)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_SPARSE_FILE)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_SYSTEM)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_TEMPORARY)) return NULL;
+    if (PyModule_AddIntMacro(m, FILE_ATTRIBUTE_VIRTUAL)) return NULL;
+#endif
+
     return m;
 }
 
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1417,6 +1417,7 @@
    Therefore, we implement our own stat, based on the Win32 API directly.
 */
 #define HAVE_STAT_NSEC 1
+#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1
 
 struct win32_stat{
     unsigned long st_dev;
@@ -1433,6 +1434,7 @@
     int st_mtime_nsec;
     time_t st_ctime;
     int st_ctime_nsec;
+    unsigned long st_file_attributes;
 };
 
 static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */
@@ -1497,6 +1499,7 @@
         /* now set the bits that make this a symlink */
         result->st_mode |= S_IFLNK;
     }
+    result->st_file_attributes = info->dwFileAttributes;
 
     return 0;
 }
@@ -1961,6 +1964,9 @@
 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
     {"st_birthtime",   "time of creation"},
 #endif
+#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
+    {"st_file_attributes", "Windows file attribute bits"},
+#endif
     {0}
 };
 
@@ -2000,6 +2006,12 @@
 #define ST_BIRTHTIME_IDX ST_GEN_IDX
 #endif
 
+#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
+#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_IDX+1)
+#else
+#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_IDX
+#endif
+
 static PyStructSequence_Desc stat_result_desc = {
     "stat_result", /* name */
     stat_result__doc__, /* doc */
@@ -2267,6 +2279,10 @@
     PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX,
                               PyLong_FromLong((long)st->st_flags));
 #endif
+#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
+    PyStructSequence_SET_ITEM(v, ST_FILE_ATTRIBUTES_IDX,
+                              PyLong_FromUnsignedLong(st->st_file_attributes));
+#endif
 
     if (PyErr_Occurred()) {
         Py_DECREF(v);

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


More information about the Python-checkins mailing list