]> granicus.if.org Git - python/commitdiff
Issue #21741: Add st_file_attributes to os.stat_result on Windows.
authorZachary Ware <zachary.ware@gmail.com>
Thu, 19 Jun 2014 14:46:37 +0000 (09:46 -0500)
committerZachary Ware <zachary.ware@gmail.com>
Thu, 19 Jun 2014 14:46:37 +0000 (09:46 -0500)
Patch by Ben Hoyt.

Doc/library/os.rst
Doc/library/stat.rst
Doc/whatsnew/3.5.rst
Lib/stat.py
Lib/test/test_os.py
Lib/test/test_stat.py
Misc/ACKS
Misc/NEWS
Modules/_stat.c
Modules/posixmodule.c

index 27f6f2a97327607cbb45e1e7cf4d5d919b32a36e..7575b3d25887689d698412225abe0c69a7d63aa1 100644 (file)
@@ -1905,6 +1905,11 @@ features:
    * :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 @@ features:
       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])
 
index 24769f6893217c7d94b3bb64eb3b786e94ea70e4..845b2ef7dabaf6cc19cafb0115175bd2c5b171e2 100644 (file)
@@ -126,7 +126,7 @@ Example::
    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 following flags can be used in the *flags* argument of :func:`os.chflags`:
    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
index 17b703adeb7387d6d9ba48f7a41434842bb569d1..1b4253ce7280c83d40b5f04164b76133cb493313 100644 (file)
@@ -176,6 +176,15 @@ ipaddress
   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
 ------
 
index 3eecc3e0d3483ed6759368981d821ee20853e027..46837c06dacfb8864456d600442a8c4d7d4ee0de 100644 (file)
@@ -148,6 +148,29 @@ def filemode(mode):
             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 *
index 7d5ee69c45e41efade09ac68870c683502c3c24f..f559841ccf6541e308036e295bae9d1213756546 100644 (file)
@@ -530,6 +530,28 @@ class StatAttributeTests(unittest.TestCase):
             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):
index af6ced4204041038d5991a06a2045973abc138a6..f1a5938a39fefbee499a3bc02c730362a7426312 100644 (file)
@@ -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 @@ class TestFilemode:
         '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 @@ class TestFilemode:
             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
index 52c00f86aed5509aa79d6ac559ef72023a41d8bf..6fff1e114e160dfb8f1ca0ea980bc368c15e457c 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -580,6 +580,7 @@ Alan Hourihane
 Ken Howard
 Brad Howes
 Mike Hoy
+Ben Hoyt
 Chih-Hao Huang
 Christian Hudon
 Lawrence Hudson
index 0ed980a1ebdd5b9b70386a2f0b16c92c58b54f20..f6491340d33b2ca7315ff5a22d67a00288c45e1a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -103,6 +103,9 @@ Core and Builtins
 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.
 
index a301fa8840c57a733f80cff1815b10f79c8fc7bd..f6cb303500cd1401ca08112ec43548a1f9a189af 100644 (file)
@@ -27,9 +27,21 @@ extern "C" {
 #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_SIZE\n\
 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 @@ PyInit__stat(void)
     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;
 }
 
index 916be81e431619729babb3876135cb42027a7e35..899618f2665b9bd8974417a5408a37006b7df76a 100644 (file)
@@ -1417,6 +1417,7 @@ win32_wchdir(LPCWSTR path)
    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 @@ struct win32_stat{
     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 @@ attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag, stru
         /* now set the bits that make this a symlink */
         result->st_mode |= S_IFLNK;
     }
+    result->st_file_attributes = info->dwFileAttributes;
 
     return 0;
 }
@@ -1960,6 +1963,9 @@ static PyStructSequence_Field stat_result_fields[] = {
 #endif
 #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 @@ static PyStructSequence_Field stat_result_fields[] = {
 #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 @@ _pystat_fromstructstat(STRUCT_STAT *st)
     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);