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_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])
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
23
Lib/stat.py
23
Lib/stat.py
|
@ -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 *
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue