Issue #21741: Add st_file_attributes to os.stat_result on Windows.
Patch by Ben Hoyt.
This commit is contained in:
parent
6ef1202eb9
commit
63f277b694
|
@ -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])
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
------
|
||||
|
||||
|
|
23
Lib/stat.py
23
Lib/stat.py
|
@ -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 *
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -580,6 +580,7 @@ Alan Hourihane
|
|||
Ken Howard
|
||||
Brad Howes
|
||||
Mike Hoy
|
||||
Ben Hoyt
|
||||
Chih-Hao Huang
|
||||
Christian Hudon
|
||||
Lawrence Hudson
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue