summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/os.rst8
-rw-r--r--Doc/library/stat.rst28
-rw-r--r--Doc/whatsnew/3.5.rst9
-rw-r--r--Lib/stat.py23
-rw-r--r--Lib/test/test_os.py22
-rw-r--r--Lib/test/test_stat.py29
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_stat.c36
-rw-r--r--Modules/posixmodule.c16
10 files changed, 174 insertions, 1 deletions
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 27f6f2a..7575b3d 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -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])
diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst
index 24769f6..845b2ef 100644
--- a/Doc/library/stat.rst
+++ b/Doc/library/stat.rst
@@ -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
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index 17b703a..1b4253c 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -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
------
diff --git a/Lib/stat.py b/Lib/stat.py
index 3eecc3e..46837c0 100644
--- a/Lib/stat.py
+++ b/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 *
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 7d5ee69..f559841 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -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):
diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py
index af6ced4..f1a5938 100644
--- 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 @@ 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
diff --git a/Misc/ACKS b/Misc/ACKS
index 52c00f8..6fff1e1 100644
--- 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
diff --git a/Misc/NEWS b/Misc/NEWS
index 0ed980a..f649134 100644
--- 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.
diff --git a/Modules/_stat.c b/Modules/_stat.c
index a301fa8..f6cb303 100644
--- a/Modules/_stat.c
+++ b/Modules/_stat.c
@@ -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;
}
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 916be81..899618f 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -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;
}
@@ -1961,6 +1964,9 @@ static PyStructSequence_Field stat_result_fields[] = {
#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);