gh-113666: Adding missing UF_ and SF_ flags to module 'stat' (#113667)

Add some constants to module 'stat' that are used on macOS.

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Ronald Oussoren 2024-01-15 12:22:43 +01:00 committed by GitHub
parent 892155d736
commit 2010d45327
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 217 additions and 8 deletions

View File

@ -350,6 +350,12 @@ The following flags can also be used in the *mode* argument of :func:`os.chmod`:
The following flags can be used in the *flags* argument of :func:`os.chflags`:
.. data:: UF_SETTABLE
All user settable flags.
.. versionadded: 3.13
.. data:: UF_NODUMP
Do not dump the file.
@ -374,10 +380,44 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`:
The file is stored compressed (macOS 10.6+).
.. data:: UF_TRACKED
Used for handling document IDs (macOS)
.. versionadded: 3.13
.. data:: UF_DATAVAULT
The file needs an entitlement for reading or writing (macOS 10.13+)
.. versionadded: 3.13
.. data:: UF_HIDDEN
The file should not be displayed in a GUI (macOS 10.5+).
.. data:: SF_SETTABLE
All super-user changeable flags
.. versionadded: 3.13
.. data:: SF_SUPPORTED
All super-user supported flags
.. availability:: macOS
.. versionadded: 3.13
.. data:: SF_SYNTHETIC
All super-user read-only synthetic flags
.. availability:: macOS
.. versionadded: 3.13
.. data:: SF_ARCHIVED
The file may be archived.
@ -390,6 +430,12 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`:
The file may only be appended to.
.. data:: SF_RESTRICTED
The file needs an entitlement to write to (macOS 10.13+)
.. versionadded: 3.13
.. data:: SF_NOUNLINK
The file may not be renamed or deleted.
@ -398,6 +444,18 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`:
The file is a snapshot file.
.. data:: SF_FIRMLINK
The file is a firmlink (macOS 10.15+)
.. versionadded: 3.13
.. data:: SF_DATALESS
The file is a dataless object (macOS 10.15+)
.. versionadded: 3.13
See the \*BSD or macOS systems man page :manpage:`chflags(2)` for more information.
On Windows, the following file attribute constants are available for use when

View File

@ -2,6 +2,7 @@
Suggested usage: from stat import *
"""
import sys
# Indices for stat struct members in the tuple returned by os.stat()
@ -110,19 +111,25 @@ S_IWOTH = 0o0002 # write by others
S_IXOTH = 0o0001 # execute by others
# Names for file flags
UF_SETTABLE = 0x0000ffff # owner settable flags
UF_NODUMP = 0x00000001 # do not dump file
UF_IMMUTABLE = 0x00000002 # file may not be changed
UF_APPEND = 0x00000004 # file may only be appended to
UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack
UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted
UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed
UF_HIDDEN = 0x00008000 # OS X: file should not be displayed
UF_COMPRESSED = 0x00000020 # macOS: file is compressed
UF_TRACKED = 0x00000040 # macOS: used for handling document IDs
UF_DATAVAULT = 0x00000080 # macOS: entitlement needed for I/O
UF_HIDDEN = 0x00008000 # macOS: file should not be displayed
SF_SETTABLE = 0xffff0000 # superuser settable flags
SF_ARCHIVED = 0x00010000 # file may be archived
SF_IMMUTABLE = 0x00020000 # file may not be changed
SF_APPEND = 0x00040000 # file may only be appended to
SF_RESTRICTED = 0x00080000 # macOS: entitlement needed for writing
SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted
SF_SNAPSHOT = 0x00200000 # file is a snapshot file
SF_FIRMLINK = 0x00800000 # macOS: file is a firmlink
SF_DATALESS = 0x40000000 # macOS: file is a dataless object
_filemode_table = (

View File

@ -15,8 +15,10 @@ class TestFilemode:
statmod = None
file_flags = {'SF_APPEND', 'SF_ARCHIVED', 'SF_IMMUTABLE', 'SF_NOUNLINK',
'SF_SNAPSHOT', 'UF_APPEND', 'UF_COMPRESSED', 'UF_HIDDEN',
'UF_IMMUTABLE', 'UF_NODUMP', 'UF_NOUNLINK', 'UF_OPAQUE'}
'SF_SNAPSHOT', 'SF_SETTABLE', 'SF_RESTRICTED', 'SF_FIRMLINK',
'SF_DATALESS', 'UF_APPEND', 'UF_COMPRESSED', 'UF_HIDDEN',
'UF_IMMUTABLE', 'UF_NODUMP', 'UF_NOUNLINK', 'UF_OPAQUE',
'UF_SETTABLE', 'UF_TRACKED', 'UF_DATAVAULT'}
formats = {'S_IFBLK', 'S_IFCHR', 'S_IFDIR', 'S_IFIFO', 'S_IFLNK',
'S_IFREG', 'S_IFSOCK', 'S_IFDOOR', 'S_IFPORT', 'S_IFWHT'}
@ -239,6 +241,18 @@ class TestFilemode:
self.assertTrue(callable(func))
self.assertEqual(func(0), 0)
def test_flags_consistent(self):
self.assertFalse(self.statmod.UF_SETTABLE & self.statmod.SF_SETTABLE)
for flag in self.file_flags:
if flag.startswith("UF"):
self.assertTrue(getattr(self.statmod, flag) & self.statmod.UF_SETTABLE, f"{flag} not in UF_SETTABLE")
elif sys.platform == 'darwin' and self.statmod is c_stat and flag == 'SF_DATALESS':
self.assertTrue(self.statmod.SF_DATALESS & self.statmod.SF_SYNTHETIC, "SF_DATALESS not in SF_SYNTHETIC")
self.assertFalse(self.statmod.SF_DATALESS & self.statmod.SF_SETTABLE, "SF_DATALESS in SF_SETTABLE")
else:
self.assertTrue(getattr(self.statmod, flag) & self.statmod.SF_SETTABLE, f"{flag} notin SF_SETTABLE")
@unittest.skipUnless(sys.platform == "win32",
"FILE_ATTRIBUTE_* constants are Win32 specific")
def test_file_attribute_constants(self):
@ -247,6 +261,66 @@ class TestFilemode:
modvalue = getattr(self.statmod, key)
self.assertEqual(value, modvalue, key)
@unittest.skipUnless(sys.platform == "darwin", "macOS system check")
def test_macosx_attribute_values(self):
self.assertEqual(self.statmod.UF_SETTABLE, 0x0000ffff)
self.assertEqual(self.statmod.UF_NODUMP, 0x00000001)
self.assertEqual(self.statmod.UF_IMMUTABLE, 0x00000002)
self.assertEqual(self.statmod.UF_APPEND, 0x00000004)
self.assertEqual(self.statmod.UF_OPAQUE, 0x00000008)
self.assertEqual(self.statmod.UF_COMPRESSED, 0x00000020)
self.assertEqual(self.statmod.UF_TRACKED, 0x00000040)
self.assertEqual(self.statmod.UF_DATAVAULT, 0x00000080)
self.assertEqual(self.statmod.UF_HIDDEN, 0x00008000)
if self.statmod is c_stat:
self.assertEqual(self.statmod.SF_SUPPORTED, 0x009f0000)
self.assertEqual(self.statmod.SF_SETTABLE, 0x3fff0000)
self.assertEqual(self.statmod.SF_SYNTHETIC, 0xc0000000)
else:
self.assertEqual(self.statmod.SF_SETTABLE, 0xffff0000)
self.assertEqual(self.statmod.SF_ARCHIVED, 0x00010000)
self.assertEqual(self.statmod.SF_IMMUTABLE, 0x00020000)
self.assertEqual(self.statmod.SF_APPEND, 0x00040000)
self.assertEqual(self.statmod.SF_RESTRICTED, 0x00080000)
self.assertEqual(self.statmod.SF_NOUNLINK, 0x00100000)
self.assertEqual(self.statmod.SF_FIRMLINK, 0x00800000)
self.assertEqual(self.statmod.SF_DATALESS, 0x40000000)
self.assertFalse(isinstance(self.statmod.S_IFMT, int))
self.assertEqual(self.statmod.S_IFIFO, 0o010000)
self.assertEqual(self.statmod.S_IFCHR, 0o020000)
self.assertEqual(self.statmod.S_IFDIR, 0o040000)
self.assertEqual(self.statmod.S_IFBLK, 0o060000)
self.assertEqual(self.statmod.S_IFREG, 0o100000)
self.assertEqual(self.statmod.S_IFLNK, 0o120000)
self.assertEqual(self.statmod.S_IFSOCK, 0o140000)
if self.statmod is c_stat:
self.assertEqual(self.statmod.S_IFWHT, 0o160000)
self.assertEqual(self.statmod.S_IRWXU, 0o000700)
self.assertEqual(self.statmod.S_IRUSR, 0o000400)
self.assertEqual(self.statmod.S_IWUSR, 0o000200)
self.assertEqual(self.statmod.S_IXUSR, 0o000100)
self.assertEqual(self.statmod.S_IRWXG, 0o000070)
self.assertEqual(self.statmod.S_IRGRP, 0o000040)
self.assertEqual(self.statmod.S_IWGRP, 0o000020)
self.assertEqual(self.statmod.S_IXGRP, 0o000010)
self.assertEqual(self.statmod.S_IRWXO, 0o000007)
self.assertEqual(self.statmod.S_IROTH, 0o000004)
self.assertEqual(self.statmod.S_IWOTH, 0o000002)
self.assertEqual(self.statmod.S_IXOTH, 0o000001)
self.assertEqual(self.statmod.S_ISUID, 0o004000)
self.assertEqual(self.statmod.S_ISGID, 0o002000)
self.assertEqual(self.statmod.S_ISVTX, 0o001000)
self.assertFalse(hasattr(self.statmod, "S_ISTXT"))
self.assertEqual(self.statmod.S_IREAD, self.statmod.S_IRUSR)
self.assertEqual(self.statmod.S_IWRITE, self.statmod.S_IWUSR)
self.assertEqual(self.statmod.S_IEXEC, self.statmod.S_IXUSR)
@unittest.skipIf(c_stat is None, 'need _stat extension')
class TestFilemodeCStat(TestFilemode, unittest.TestCase):

View File

@ -0,0 +1,5 @@
Add the following constants to module :mod:`stat`: ``UF_SETTABLE``,
``UF_TRACKED``, ``UF_DATAVAULT``, ``SF_SUPPORTED``, ``SF_SETTABLE``,
``SF_SYNTHETIC``, ``SF_RESTRICTED``, ``SF_FIRMLINK`` and ``SF_DATALESS``.
The values ``UF_SETTABLE``, ``SF_SUPPORTED``, ``SF_SETTABLE`` and
``SF_SYNTHETIC`` are only available on macOS.

View File

@ -202,6 +202,10 @@ typedef unsigned short mode_t;
/* Names for file flags */
#ifndef UF_SETTABLE
# define UF_SETTABLE 0x0000ffff
#endif
#ifndef UF_NODUMP
# define UF_NODUMP 0x00000001
#endif
@ -226,10 +230,22 @@ typedef unsigned short mode_t;
# define UF_COMPRESSED 0x00000020
#endif
#ifndef UF_TRACKED
# define UF_TRACKED 0x00000040
#endif
#ifndef UF_DATAVAULT
# define UF_DATAVAULT 0x00000080
#endif
#ifndef UF_HIDDEN
# define UF_HIDDEN 0x00008000
#endif
#ifndef SF_SETTABLE
# define SF_SETTABLE 0xffff0000
#endif
#ifndef SF_ARCHIVED
# define SF_ARCHIVED 0x00010000
#endif
@ -250,6 +266,30 @@ typedef unsigned short mode_t;
# define SF_SNAPSHOT 0x00200000
#endif
#ifndef SF_FIRMLINK
# define SF_FIRMLINK 0x00800000
#endif
#ifndef SF_DATALESS
# define SF_DATALESS 0x40000000
#endif
#if defined(__APPLE__) && !defined(SF_SUPPORTED)
/* On older macOS versions the definition of SF_SUPPORTED is different
* from that on newer versions.
*
* Provide a consistent experience by redefining.
*
* None of bit bits set in the actual SF_SUPPORTED but not in this
* definition are defined on these versions of macOS.
*/
# undef SF_SETTABLE
# define SF_SUPPORTED 0x009f0000
# define SF_SETTABLE 0x3fff0000
# define SF_SYNTHETIC 0xc0000000
#endif
static mode_t
_PyLong_AsMode_t(PyObject *op)
{
@ -467,18 +507,29 @@ S_IWOTH: write by others\n\
S_IXOTH: execute by others\n\
\n"
"UF_NODUMP: do not dump file\n\
"UF_SETTABLE: mask of owner changable flags\n\
UF_NODUMP: do not dump file\n\
UF_IMMUTABLE: file may not be changed\n\
UF_APPEND: file may only be appended to\n\
UF_OPAQUE: directory is opaque when viewed through a union stack\n\
UF_NOUNLINK: file may not be renamed or deleted\n\
UF_COMPRESSED: OS X: file is hfs-compressed\n\
UF_HIDDEN: OS X: file should not be displayed\n\
UF_COMPRESSED: macOS: file is hfs-compressed\n\
UF_TRACKED: used for dealing with document IDs\n\
UF_DATAVAULT: entitlement required for reading and writing\n\
UF_HIDDEN: macOS: file should not be displayed\n\
SF_SETTABLE: mask of super user changeable flags\n\
SF_ARCHIVED: file may be archived\n\
SF_IMMUTABLE: file may not be changed\n\
SF_APPEND: file may only be appended to\n\
SF_RESTRICTED: entitlement required for writing\n\
SF_NOUNLINK: file may not be renamed or deleted\n\
SF_SNAPSHOT: file is a snapshot file\n\
SF_FIRMLINK: file is a firmlink\n\
SF_DATALESS: file is a dataless object\n\
\n\
On macOS:\n\
SF_SUPPORTED: mask of super user supported flags\n\
SF_SYNTHETIC: mask of read-only synthetic flags\n\
\n"
"ST_MODE\n\
@ -543,18 +594,32 @@ stat_exec(PyObject *module)
ADD_INT_MACRO(module, S_IWOTH);
ADD_INT_MACRO(module, S_IXOTH);
ADD_INT_MACRO(module, UF_SETTABLE);
ADD_INT_MACRO(module, UF_NODUMP);
ADD_INT_MACRO(module, UF_IMMUTABLE);
ADD_INT_MACRO(module, UF_APPEND);
ADD_INT_MACRO(module, UF_OPAQUE);
ADD_INT_MACRO(module, UF_NOUNLINK);
ADD_INT_MACRO(module, UF_COMPRESSED);
ADD_INT_MACRO(module, UF_TRACKED);
ADD_INT_MACRO(module, UF_DATAVAULT);
ADD_INT_MACRO(module, UF_HIDDEN);
ADD_INT_MACRO(module, SF_SETTABLE);
ADD_INT_MACRO(module, SF_ARCHIVED);
ADD_INT_MACRO(module, SF_IMMUTABLE);
ADD_INT_MACRO(module, SF_APPEND);
ADD_INT_MACRO(module, SF_NOUNLINK);
ADD_INT_MACRO(module, SF_SNAPSHOT);
ADD_INT_MACRO(module, SF_FIRMLINK);
ADD_INT_MACRO(module, SF_DATALESS);
#ifdef SF_SUPPORTED
ADD_INT_MACRO(module, SF_SUPPORTED);
#endif
#ifdef SF_SYNTHETIC
ADD_INT_MACRO(module, SF_SYNTHETIC);
#endif
const char* st_constants[] = {
"ST_MODE",