Skip to content

Commit 2010d45

Browse files
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 <[email protected]>
1 parent 892155d commit 2010d45

File tree

5 files changed

+217
-8
lines changed

5 files changed

+217
-8
lines changed

Doc/library/stat.rst

+58
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,12 @@ The following flags can also be used in the *mode* argument of :func:`os.chmod`:
350350

351351
The following flags can be used in the *flags* argument of :func:`os.chflags`:
352352

353+
.. data:: UF_SETTABLE
354+
355+
All user settable flags.
356+
357+
.. versionadded: 3.13
358+
353359
.. data:: UF_NODUMP
354360

355361
Do not dump the file.
@@ -374,10 +380,44 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`:
374380

375381
The file is stored compressed (macOS 10.6+).
376382

383+
.. data:: UF_TRACKED
384+
385+
Used for handling document IDs (macOS)
386+
387+
.. versionadded: 3.13
388+
389+
.. data:: UF_DATAVAULT
390+
391+
The file needs an entitlement for reading or writing (macOS 10.13+)
392+
393+
.. versionadded: 3.13
394+
377395
.. data:: UF_HIDDEN
378396

379397
The file should not be displayed in a GUI (macOS 10.5+).
380398

399+
.. data:: SF_SETTABLE
400+
401+
All super-user changeable flags
402+
403+
.. versionadded: 3.13
404+
405+
.. data:: SF_SUPPORTED
406+
407+
All super-user supported flags
408+
409+
.. availability:: macOS
410+
411+
.. versionadded: 3.13
412+
413+
.. data:: SF_SYNTHETIC
414+
415+
All super-user read-only synthetic flags
416+
417+
.. availability:: macOS
418+
419+
.. versionadded: 3.13
420+
381421
.. data:: SF_ARCHIVED
382422

383423
The file may be archived.
@@ -390,6 +430,12 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`:
390430

391431
The file may only be appended to.
392432

433+
.. data:: SF_RESTRICTED
434+
435+
The file needs an entitlement to write to (macOS 10.13+)
436+
437+
.. versionadded: 3.13
438+
393439
.. data:: SF_NOUNLINK
394440

395441
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`:
398444

399445
The file is a snapshot file.
400446

447+
.. data:: SF_FIRMLINK
448+
449+
The file is a firmlink (macOS 10.15+)
450+
451+
.. versionadded: 3.13
452+
453+
.. data:: SF_DATALESS
454+
455+
The file is a dataless object (macOS 10.15+)
456+
457+
.. versionadded: 3.13
458+
401459
See the \*BSD or macOS systems man page :manpage:`chflags(2)` for more information.
402460

403461
On Windows, the following file attribute constants are available for use when

Lib/stat.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
Suggested usage: from stat import *
44
"""
5+
import sys
56

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

@@ -110,19 +111,25 @@ def S_ISWHT(mode):
110111
S_IXOTH = 0o0001 # execute by others
111112

112113
# Names for file flags
113-
114+
UF_SETTABLE = 0x0000ffff # owner settable flags
114115
UF_NODUMP = 0x00000001 # do not dump file
115116
UF_IMMUTABLE = 0x00000002 # file may not be changed
116117
UF_APPEND = 0x00000004 # file may only be appended to
117118
UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack
118119
UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted
119-
UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed
120-
UF_HIDDEN = 0x00008000 # OS X: file should not be displayed
120+
UF_COMPRESSED = 0x00000020 # macOS: file is compressed
121+
UF_TRACKED = 0x00000040 # macOS: used for handling document IDs
122+
UF_DATAVAULT = 0x00000080 # macOS: entitlement needed for I/O
123+
UF_HIDDEN = 0x00008000 # macOS: file should not be displayed
124+
SF_SETTABLE = 0xffff0000 # superuser settable flags
121125
SF_ARCHIVED = 0x00010000 # file may be archived
122126
SF_IMMUTABLE = 0x00020000 # file may not be changed
123127
SF_APPEND = 0x00040000 # file may only be appended to
128+
SF_RESTRICTED = 0x00080000 # macOS: entitlement needed for writing
124129
SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted
125130
SF_SNAPSHOT = 0x00200000 # file is a snapshot file
131+
SF_FIRMLINK = 0x00800000 # macOS: file is a firmlink
132+
SF_DATALESS = 0x40000000 # macOS: file is a dataless object
126133

127134

128135
_filemode_table = (

Lib/test/test_stat.py

+76-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ class TestFilemode:
1515
statmod = None
1616

1717
file_flags = {'SF_APPEND', 'SF_ARCHIVED', 'SF_IMMUTABLE', 'SF_NOUNLINK',
18-
'SF_SNAPSHOT', 'UF_APPEND', 'UF_COMPRESSED', 'UF_HIDDEN',
19-
'UF_IMMUTABLE', 'UF_NODUMP', 'UF_NOUNLINK', 'UF_OPAQUE'}
18+
'SF_SNAPSHOT', 'SF_SETTABLE', 'SF_RESTRICTED', 'SF_FIRMLINK',
19+
'SF_DATALESS', 'UF_APPEND', 'UF_COMPRESSED', 'UF_HIDDEN',
20+
'UF_IMMUTABLE', 'UF_NODUMP', 'UF_NOUNLINK', 'UF_OPAQUE',
21+
'UF_SETTABLE', 'UF_TRACKED', 'UF_DATAVAULT'}
2022

2123
formats = {'S_IFBLK', 'S_IFCHR', 'S_IFDIR', 'S_IFIFO', 'S_IFLNK',
2224
'S_IFREG', 'S_IFSOCK', 'S_IFDOOR', 'S_IFPORT', 'S_IFWHT'}
@@ -239,6 +241,18 @@ def test_module_attributes(self):
239241
self.assertTrue(callable(func))
240242
self.assertEqual(func(0), 0)
241243

244+
def test_flags_consistent(self):
245+
self.assertFalse(self.statmod.UF_SETTABLE & self.statmod.SF_SETTABLE)
246+
247+
for flag in self.file_flags:
248+
if flag.startswith("UF"):
249+
self.assertTrue(getattr(self.statmod, flag) & self.statmod.UF_SETTABLE, f"{flag} not in UF_SETTABLE")
250+
elif sys.platform == 'darwin' and self.statmod is c_stat and flag == 'SF_DATALESS':
251+
self.assertTrue(self.statmod.SF_DATALESS & self.statmod.SF_SYNTHETIC, "SF_DATALESS not in SF_SYNTHETIC")
252+
self.assertFalse(self.statmod.SF_DATALESS & self.statmod.SF_SETTABLE, "SF_DATALESS in SF_SETTABLE")
253+
else:
254+
self.assertTrue(getattr(self.statmod, flag) & self.statmod.SF_SETTABLE, f"{flag} notin SF_SETTABLE")
255+
242256
@unittest.skipUnless(sys.platform == "win32",
243257
"FILE_ATTRIBUTE_* constants are Win32 specific")
244258
def test_file_attribute_constants(self):
@@ -247,6 +261,66 @@ def test_file_attribute_constants(self):
247261
modvalue = getattr(self.statmod, key)
248262
self.assertEqual(value, modvalue, key)
249263

264+
@unittest.skipUnless(sys.platform == "darwin", "macOS system check")
265+
def test_macosx_attribute_values(self):
266+
self.assertEqual(self.statmod.UF_SETTABLE, 0x0000ffff)
267+
self.assertEqual(self.statmod.UF_NODUMP, 0x00000001)
268+
self.assertEqual(self.statmod.UF_IMMUTABLE, 0x00000002)
269+
self.assertEqual(self.statmod.UF_APPEND, 0x00000004)
270+
self.assertEqual(self.statmod.UF_OPAQUE, 0x00000008)
271+
self.assertEqual(self.statmod.UF_COMPRESSED, 0x00000020)
272+
self.assertEqual(self.statmod.UF_TRACKED, 0x00000040)
273+
self.assertEqual(self.statmod.UF_DATAVAULT, 0x00000080)
274+
self.assertEqual(self.statmod.UF_HIDDEN, 0x00008000)
275+
276+
if self.statmod is c_stat:
277+
self.assertEqual(self.statmod.SF_SUPPORTED, 0x009f0000)
278+
self.assertEqual(self.statmod.SF_SETTABLE, 0x3fff0000)
279+
self.assertEqual(self.statmod.SF_SYNTHETIC, 0xc0000000)
280+
else:
281+
self.assertEqual(self.statmod.SF_SETTABLE, 0xffff0000)
282+
self.assertEqual(self.statmod.SF_ARCHIVED, 0x00010000)
283+
self.assertEqual(self.statmod.SF_IMMUTABLE, 0x00020000)
284+
self.assertEqual(self.statmod.SF_APPEND, 0x00040000)
285+
self.assertEqual(self.statmod.SF_RESTRICTED, 0x00080000)
286+
self.assertEqual(self.statmod.SF_NOUNLINK, 0x00100000)
287+
self.assertEqual(self.statmod.SF_FIRMLINK, 0x00800000)
288+
self.assertEqual(self.statmod.SF_DATALESS, 0x40000000)
289+
290+
self.assertFalse(isinstance(self.statmod.S_IFMT, int))
291+
self.assertEqual(self.statmod.S_IFIFO, 0o010000)
292+
self.assertEqual(self.statmod.S_IFCHR, 0o020000)
293+
self.assertEqual(self.statmod.S_IFDIR, 0o040000)
294+
self.assertEqual(self.statmod.S_IFBLK, 0o060000)
295+
self.assertEqual(self.statmod.S_IFREG, 0o100000)
296+
self.assertEqual(self.statmod.S_IFLNK, 0o120000)
297+
self.assertEqual(self.statmod.S_IFSOCK, 0o140000)
298+
299+
if self.statmod is c_stat:
300+
self.assertEqual(self.statmod.S_IFWHT, 0o160000)
301+
302+
self.assertEqual(self.statmod.S_IRWXU, 0o000700)
303+
self.assertEqual(self.statmod.S_IRUSR, 0o000400)
304+
self.assertEqual(self.statmod.S_IWUSR, 0o000200)
305+
self.assertEqual(self.statmod.S_IXUSR, 0o000100)
306+
self.assertEqual(self.statmod.S_IRWXG, 0o000070)
307+
self.assertEqual(self.statmod.S_IRGRP, 0o000040)
308+
self.assertEqual(self.statmod.S_IWGRP, 0o000020)
309+
self.assertEqual(self.statmod.S_IXGRP, 0o000010)
310+
self.assertEqual(self.statmod.S_IRWXO, 0o000007)
311+
self.assertEqual(self.statmod.S_IROTH, 0o000004)
312+
self.assertEqual(self.statmod.S_IWOTH, 0o000002)
313+
self.assertEqual(self.statmod.S_IXOTH, 0o000001)
314+
self.assertEqual(self.statmod.S_ISUID, 0o004000)
315+
self.assertEqual(self.statmod.S_ISGID, 0o002000)
316+
self.assertEqual(self.statmod.S_ISVTX, 0o001000)
317+
318+
self.assertFalse(hasattr(self.statmod, "S_ISTXT"))
319+
self.assertEqual(self.statmod.S_IREAD, self.statmod.S_IRUSR)
320+
self.assertEqual(self.statmod.S_IWRITE, self.statmod.S_IWUSR)
321+
self.assertEqual(self.statmod.S_IEXEC, self.statmod.S_IXUSR)
322+
323+
250324

251325
@unittest.skipIf(c_stat is None, 'need _stat extension')
252326
class TestFilemodeCStat(TestFilemode, unittest.TestCase):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Add the following constants to module :mod:`stat`: ``UF_SETTABLE``,
2+
``UF_TRACKED``, ``UF_DATAVAULT``, ``SF_SUPPORTED``, ``SF_SETTABLE``,
3+
``SF_SYNTHETIC``, ``SF_RESTRICTED``, ``SF_FIRMLINK`` and ``SF_DATALESS``.
4+
The values ``UF_SETTABLE``, ``SF_SUPPORTED``, ``SF_SETTABLE`` and
5+
``SF_SYNTHETIC`` are only available on macOS.

Modules/_stat.c

+68-3
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ typedef unsigned short mode_t;
202202

203203

204204
/* Names for file flags */
205+
#ifndef UF_SETTABLE
206+
# define UF_SETTABLE 0x0000ffff
207+
#endif
208+
205209
#ifndef UF_NODUMP
206210
# define UF_NODUMP 0x00000001
207211
#endif
@@ -226,10 +230,22 @@ typedef unsigned short mode_t;
226230
# define UF_COMPRESSED 0x00000020
227231
#endif
228232

233+
#ifndef UF_TRACKED
234+
# define UF_TRACKED 0x00000040
235+
#endif
236+
237+
#ifndef UF_DATAVAULT
238+
# define UF_DATAVAULT 0x00000080
239+
#endif
240+
229241
#ifndef UF_HIDDEN
230242
# define UF_HIDDEN 0x00008000
231243
#endif
232244

245+
#ifndef SF_SETTABLE
246+
# define SF_SETTABLE 0xffff0000
247+
#endif
248+
233249
#ifndef SF_ARCHIVED
234250
# define SF_ARCHIVED 0x00010000
235251
#endif
@@ -250,6 +266,30 @@ typedef unsigned short mode_t;
250266
# define SF_SNAPSHOT 0x00200000
251267
#endif
252268

269+
#ifndef SF_FIRMLINK
270+
# define SF_FIRMLINK 0x00800000
271+
#endif
272+
273+
#ifndef SF_DATALESS
274+
# define SF_DATALESS 0x40000000
275+
#endif
276+
277+
#if defined(__APPLE__) && !defined(SF_SUPPORTED)
278+
/* On older macOS versions the definition of SF_SUPPORTED is different
279+
* from that on newer versions.
280+
*
281+
* Provide a consistent experience by redefining.
282+
*
283+
* None of bit bits set in the actual SF_SUPPORTED but not in this
284+
* definition are defined on these versions of macOS.
285+
*/
286+
# undef SF_SETTABLE
287+
# define SF_SUPPORTED 0x009f0000
288+
# define SF_SETTABLE 0x3fff0000
289+
# define SF_SYNTHETIC 0xc0000000
290+
#endif
291+
292+
253293
static mode_t
254294
_PyLong_AsMode_t(PyObject *op)
255295
{
@@ -467,18 +507,29 @@ S_IWOTH: write by others\n\
467507
S_IXOTH: execute by others\n\
468508
\n"
469509

470-
"UF_NODUMP: do not dump file\n\
510+
"UF_SETTABLE: mask of owner changable flags\n\
511+
UF_NODUMP: do not dump file\n\
471512
UF_IMMUTABLE: file may not be changed\n\
472513
UF_APPEND: file may only be appended to\n\
473514
UF_OPAQUE: directory is opaque when viewed through a union stack\n\
474515
UF_NOUNLINK: file may not be renamed or deleted\n\
475-
UF_COMPRESSED: OS X: file is hfs-compressed\n\
476-
UF_HIDDEN: OS X: file should not be displayed\n\
516+
UF_COMPRESSED: macOS: file is hfs-compressed\n\
517+
UF_TRACKED: used for dealing with document IDs\n\
518+
UF_DATAVAULT: entitlement required for reading and writing\n\
519+
UF_HIDDEN: macOS: file should not be displayed\n\
520+
SF_SETTABLE: mask of super user changeable flags\n\
477521
SF_ARCHIVED: file may be archived\n\
478522
SF_IMMUTABLE: file may not be changed\n\
479523
SF_APPEND: file may only be appended to\n\
524+
SF_RESTRICTED: entitlement required for writing\n\
480525
SF_NOUNLINK: file may not be renamed or deleted\n\
481526
SF_SNAPSHOT: file is a snapshot file\n\
527+
SF_FIRMLINK: file is a firmlink\n\
528+
SF_DATALESS: file is a dataless object\n\
529+
\n\
530+
On macOS:\n\
531+
SF_SUPPORTED: mask of super user supported flags\n\
532+
SF_SYNTHETIC: mask of read-only synthetic flags\n\
482533
\n"
483534

484535
"ST_MODE\n\
@@ -543,18 +594,32 @@ stat_exec(PyObject *module)
543594
ADD_INT_MACRO(module, S_IWOTH);
544595
ADD_INT_MACRO(module, S_IXOTH);
545596

597+
ADD_INT_MACRO(module, UF_SETTABLE);
546598
ADD_INT_MACRO(module, UF_NODUMP);
547599
ADD_INT_MACRO(module, UF_IMMUTABLE);
548600
ADD_INT_MACRO(module, UF_APPEND);
549601
ADD_INT_MACRO(module, UF_OPAQUE);
550602
ADD_INT_MACRO(module, UF_NOUNLINK);
551603
ADD_INT_MACRO(module, UF_COMPRESSED);
604+
ADD_INT_MACRO(module, UF_TRACKED);
605+
ADD_INT_MACRO(module, UF_DATAVAULT);
552606
ADD_INT_MACRO(module, UF_HIDDEN);
607+
ADD_INT_MACRO(module, SF_SETTABLE);
553608
ADD_INT_MACRO(module, SF_ARCHIVED);
554609
ADD_INT_MACRO(module, SF_IMMUTABLE);
555610
ADD_INT_MACRO(module, SF_APPEND);
556611
ADD_INT_MACRO(module, SF_NOUNLINK);
557612
ADD_INT_MACRO(module, SF_SNAPSHOT);
613+
ADD_INT_MACRO(module, SF_FIRMLINK);
614+
ADD_INT_MACRO(module, SF_DATALESS);
615+
616+
#ifdef SF_SUPPORTED
617+
ADD_INT_MACRO(module, SF_SUPPORTED);
618+
#endif
619+
#ifdef SF_SYNTHETIC
620+
ADD_INT_MACRO(module, SF_SYNTHETIC);
621+
#endif
622+
558623

559624
const char* st_constants[] = {
560625
"ST_MODE",

0 commit comments

Comments
 (0)