Skip to content

Commit

Permalink
pythongh-113666: Adding missing UF_ and SF_ flags to module 'stat' (p…
Browse files Browse the repository at this point in the history
…ython#113667)

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

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
  • Loading branch information
2 people authored and kulikjak committed Jan 22, 2024
1 parent 391140e commit 73c103f
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 8 deletions.
58 changes: 58 additions & 0 deletions Doc/library/stat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand Down
13 changes: 10 additions & 3 deletions Lib/stat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Suggested usage: from stat import *
"""
import sys

# Indices for stat struct members in the tuple returned by os.stat()

Expand Down Expand Up @@ -110,19 +111,25 @@ def S_ISWHT(mode):
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 = (
Expand Down
78 changes: 76 additions & 2 deletions Lib/test/test_stat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'}
Expand Down Expand Up @@ -239,6 +241,18 @@ def test_module_attributes(self):
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):
Expand All @@ -247,6 +261,66 @@ def test_file_attribute_constants(self):
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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
71 changes: 68 additions & 3 deletions Modules/_stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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\
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit 73c103f

Please sign in to comment.