Issue #21741: Add st_file_attributes to os.stat_result on Windows.

Patch by Ben Hoyt.
This commit is contained in:
Zachary Ware 2014-06-19 09:46:37 -05:00
parent 6ef1202eb9
commit 63f277b694
10 changed files with 174 additions and 1 deletions

View File

@ -1905,6 +1905,11 @@ features:
* :attr:`st_creator` * :attr:`st_creator`
* :attr:`st_type` * :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:: .. note::
The exact meaning and resolution of the :attr:`st_atime`, 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 the :attr:`st_atime_ns`, :attr:`st_mtime_ns`,
and :attr:`st_ctime_ns` members. and :attr:`st_ctime_ns` members.
.. versionadded:: 3.5
Added the :attr:`st_file_attributes` member on Windows.
.. function:: stat_float_times([newvalue]) .. function:: stat_float_times([newvalue])

View File

@ -126,7 +126,7 @@ Example::
if __name__ == '__main__': if __name__ == '__main__':
walktree(sys.argv[1], visitfile) 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: readable string:
.. function:: filemode(mode) .. 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. The file is a snapshot file.
See the \*BSD or Mac OS systems man page :manpage:`chflags(2)` for more information. 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

View File

@ -176,6 +176,15 @@ ipaddress
network objects from existing addresses (contributed by Peter Moody network objects from existing addresses (contributed by Peter Moody
and Antoine Pitrou in :issue:`16531`). 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 shutil
------ ------

View File

@ -148,6 +148,29 @@ def filemode(mode):
perm.append("-") perm.append("-")
return "".join(perm) 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 # If available, use C implementation
try: try:
from _stat import * from _stat import *

View File

@ -530,6 +530,28 @@ class StatAttributeTests(unittest.TestCase):
os.stat(r) os.stat(r)
self.assertEqual(ctx.exception.errno, errno.EBADF) 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 from test import mapping_tests
class EnvironTests(mapping_tests.BasicTestMappingProtocol): class EnvironTests(mapping_tests.BasicTestMappingProtocol):

View File

@ -1,5 +1,6 @@
import unittest import unittest
import os import os
import sys
from test.support import TESTFN, import_fresh_module from test.support import TESTFN, import_fresh_module
c_stat = import_fresh_module('stat', fresh=['_stat']) c_stat = import_fresh_module('stat', fresh=['_stat'])
@ -52,6 +53,26 @@ class TestFilemode:
'S_IWOTH': 0o002, 'S_IWOTH': 0o002,
'S_IXOTH': 0o001} '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): def setUp(self):
try: try:
os.remove(TESTFN) os.remove(TESTFN)
@ -185,6 +206,14 @@ class TestFilemode:
self.assertTrue(callable(func)) self.assertTrue(callable(func))
self.assertEqual(func(0), 0) 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): class TestFilemodeCStat(TestFilemode, unittest.TestCase):
statmod = c_stat statmod = c_stat

View File

@ -580,6 +580,7 @@ Alan Hourihane
Ken Howard Ken Howard
Brad Howes Brad Howes
Mike Hoy Mike Hoy
Ben Hoyt
Chih-Hao Huang Chih-Hao Huang
Christian Hudon Christian Hudon
Lawrence Hudson Lawrence Hudson

View File

@ -103,6 +103,9 @@ Core and Builtins
Library 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 - Issue #21722: The distutils "upload" command now exits with a non-zero
return code when uploading fails. Patch by Martin Dengler. return code when uploading fails. Patch by Martin Dengler.

View File

@ -27,9 +27,21 @@ extern "C" {
#endif /* HAVE_SYS_STAT_H */ #endif /* HAVE_SYS_STAT_H */
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
#include <windows.h>
typedef unsigned short mode_t; 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 #endif
#ifndef FILE_ATTRIBUTE_NO_SCRUB_DATA
# define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x20000
#endif
#endif /* MS_WINDOWS */
/* From Python's stat.py */ /* From Python's stat.py */
#ifndef S_IMODE #ifndef S_IMODE
# define S_IMODE 07777 # define S_IMODE 07777
@ -473,6 +485,10 @@ ST_SIZE\n\
ST_ATIME\n\ ST_ATIME\n\
ST_MTIME\n\ ST_MTIME\n\
ST_CTIME\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_MTIME", 8)) return NULL;
if (PyModule_AddIntConstant(m, "ST_CTIME", 9)) 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; return m;
} }

View File

@ -1417,6 +1417,7 @@ win32_wchdir(LPCWSTR path)
Therefore, we implement our own stat, based on the Win32 API directly. Therefore, we implement our own stat, based on the Win32 API directly.
*/ */
#define HAVE_STAT_NSEC 1 #define HAVE_STAT_NSEC 1
#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1
struct win32_stat{ struct win32_stat{
unsigned long st_dev; unsigned long st_dev;
@ -1433,6 +1434,7 @@ struct win32_stat{
int st_mtime_nsec; int st_mtime_nsec;
time_t st_ctime; time_t st_ctime;
int st_ctime_nsec; int st_ctime_nsec;
unsigned long st_file_attributes;
}; };
static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */ 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 */ /* now set the bits that make this a symlink */
result->st_mode |= S_IFLNK; result->st_mode |= S_IFLNK;
} }
result->st_file_attributes = info->dwFileAttributes;
return 0; return 0;
} }
@ -1960,6 +1963,9 @@ static PyStructSequence_Field stat_result_fields[] = {
#endif #endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
{"st_birthtime", "time of creation"}, {"st_birthtime", "time of creation"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
{"st_file_attributes", "Windows file attribute bits"},
#endif #endif
{0} {0}
}; };
@ -2000,6 +2006,12 @@ static PyStructSequence_Field stat_result_fields[] = {
#define ST_BIRTHTIME_IDX ST_GEN_IDX #define ST_BIRTHTIME_IDX ST_GEN_IDX
#endif #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 = { static PyStructSequence_Desc stat_result_desc = {
"stat_result", /* name */ "stat_result", /* name */
stat_result__doc__, /* doc */ stat_result__doc__, /* doc */
@ -2267,6 +2279,10 @@ _pystat_fromstructstat(STRUCT_STAT *st)
PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX, PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX,
PyLong_FromLong((long)st->st_flags)); PyLong_FromLong((long)st->st_flags));
#endif #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()) { if (PyErr_Occurred()) {
Py_DECREF(v); Py_DECREF(v);