Skip to content

Commit 6f7e9fa

Browse files
committed
pythongh-99726: Adds os.statx function and associated constants
1 parent ae185fd commit 6f7e9fa

19 files changed

+755
-45
lines changed

Doc/library/os.rst

Lines changed: 135 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,18 +2839,75 @@ features:
28392839
``follow_symlinks=False`` had been specified instead of raising an error.
28402840

28412841

2842+
.. function:: statx(path, mask, *, dir_fd=None, follow_symlinks=True, flags=0)
2843+
2844+
Get selected fields of the status of a file or a file descriptor. Perform the
2845+
equivalent of a :c:func:`statx` system call on the given path. *path* may be
2846+
specified as either a string or bytes -- directly or indirectly through the
2847+
:class:`PathLike` interface -- or as an open file descriptor. Return a
2848+
:class:`stat_result` object.
2849+
2850+
*mask* is a bitwise combination of the ``STATX_*`` attributes in the
2851+
:mod:`stat` module, indicating which fields the caller intends to use. Note
2852+
that the set of fields returned may differ from what's requested, if the
2853+
operating system or file system does not support the metadata, or if it can
2854+
provide additional fields with no extra effort.
2855+
2856+
This function normally follows symlinks; to stat a symlink add the argument
2857+
``follow_symlinks=False``.
2858+
2859+
This function can support :ref:`specifying a file descriptor <path_fd>` and
2860+
:ref:`not following symlinks <follow_symlinks>`.
2861+
2862+
On Windows, passing ``follow_symlinks=False`` will disable following all
2863+
name-surrogate reparse points, which includes symlinks and directory
2864+
junctions. Other types of reparse points that do not resemble links or that
2865+
the operating system is unable to follow will be opened directly. When
2866+
following a chain of multiple links, this may result in the original link
2867+
being returned instead of the non-link that prevented full traversal. To
2868+
obtain stat results for the final path in this case, use the
2869+
:func:`os.path.realpath` function to resolve the path name as far as
2870+
possible and call :func:`lstat` on the result. This does not apply to
2871+
dangling symlinks or junction points, which will raise the usual exceptions.
2872+
2873+
.. index:: module: stat
2874+
2875+
Example::
2876+
2877+
>>> import os, stat
2878+
>>> statinfo = os.statx('somefile.txt', stat.STATX_SIZE)
2879+
>>> statinfo
2880+
os.stat_result(st_mode=33188, st_ino=0, st_dev=0,
2881+
st_nlink=1, st_uid=0, st_gid=0, st_size=264, st_atime=0,
2882+
st_mtime=0, st_ctime=0)
2883+
>>> statinfo.stx_mask & stat.STATX_SIZE
2884+
512
2885+
>>> statinfo.st_size
2886+
264
2887+
2888+
.. seealso::
2889+
2890+
:func:`stat`, :func:`fstat` and :func:`lstat` functions.
2891+
2892+
.. versionadded:: 3.12
2893+
Added ``statx`` function.
2894+
2895+
28422896
.. class:: stat_result
28432897

28442898
Object whose attributes correspond roughly to the members of the
28452899
:c:type:`stat` structure. It is used for the result of :func:`os.stat`,
2846-
:func:`os.fstat` and :func:`os.lstat`.
2900+
:func:`os.fstat`, :func:`os.lstat` and :func:`os.statx`.
28472901

28482902
Attributes:
28492903

28502904
.. attribute:: st_mode
28512905

28522906
File mode: file type and file mode bits (permissions).
28532907

2908+
This field is set with the :data:`stat.STATX_TYPE` and/or
2909+
:data:`stat.STATX_MODE` flags.
2910+
28542911
.. attribute:: st_ino
28552912

28562913
Platform dependent, but if non-zero, uniquely identifies the
@@ -2861,70 +2918,111 @@ features:
28612918
<https://msdn.microsoft.com/en-us/library/aa363788>`_ on
28622919
Windows
28632920

2921+
This field is set with :data:`stat.STATX_INO`.
2922+
28642923
.. attribute:: st_dev
28652924

28662925
Identifier of the device on which this file resides.
28672926

2927+
On Windows, this field is set with :data:`stat.STATX_INO`.
2928+
28682929
.. attribute:: st_nlink
28692930

28702931
Number of hard links.
28712932

2933+
This field is set with :data:`stat.STATX_NLINK`.
2934+
28722935
.. attribute:: st_uid
28732936

28742937
User identifier of the file owner.
28752938

2939+
This field is set with :data:`stat.STATX_UID`.
2940+
28762941
.. attribute:: st_gid
28772942

28782943
Group identifier of the file owner.
28792944

2945+
This field is set with :data:`stat.STATX_GID`.
2946+
28802947
.. attribute:: st_size
28812948

28822949
Size of the file in bytes, if it is a regular file or a symbolic link.
28832950
The size of a symbolic link is the length of the pathname it contains,
28842951
without a terminating null byte.
28852952

2953+
This field is set with :data:`stat.STATX_SIZE`.
2954+
2955+
.. attribute:: stx_mask
2956+
2957+
Flags indicating which values were set. :func`os.statx` allows specifying
2958+
a mask, though the result may include more or less than requested. Other
2959+
``stat`` functions set a default value representing the information they
2960+
return.
2961+
28862962
Timestamps:
28872963

28882964
.. attribute:: st_atime
28892965

28902966
Time of most recent access expressed in seconds.
28912967

2968+
This field is set with :data:`stat.STATX_ATIME`.
2969+
28922970
.. attribute:: st_mtime
28932971

28942972
Time of most recent content modification expressed in seconds.
28952973

2974+
This field is set with :data:`stat.STATX_MTIME`.
2975+
28962976
.. attribute:: st_ctime
28972977

28982978
Platform dependent:
28992979

29002980
* the time of most recent metadata change on Unix,
2901-
* the time of creation on Windows, expressed in seconds.
2981+
* the time of creation on Windows, expressed in seconds, except
2982+
when :data:`stat.STATX_CTIME` is in :attr:`stx_mask`, in which
2983+
case this is the time of the most recent metadata change
2984+
2985+
This field is set with :data:`stat.STATX_CTIME`.
29022986

29032987
.. attribute:: st_atime_ns
29042988

29052989
Time of most recent access expressed in nanoseconds as an integer.
29062990

2991+
This field is set with :data:`stat.STATX_ATIME`.
2992+
29072993
.. attribute:: st_mtime_ns
29082994

29092995
Time of most recent content modification expressed in nanoseconds as an
29102996
integer.
29112997

2998+
This field is set with :data:`stat.STATX_MTIME`.
2999+
29123000
.. attribute:: st_ctime_ns
29133001

29143002
Platform dependent:
29153003

29163004
* the time of most recent metadata change on Unix,
29173005
* the time of creation on Windows, expressed in nanoseconds as an
2918-
integer.
3006+
integer, except when :data:`stat.STATX_CTIME` is in :attr:`stx_mask`,
3007+
in which case this is the time of the most recent metadata change
3008+
3009+
This field is set with :data:`stat.STATX_CTIME`.
3010+
3011+
.. attribute:: st_birthtime
3012+
3013+
Time of file creation, if available. The attribute may not be present if
3014+
your operating system does not support the field.
3015+
3016+
This field is set with :data:`stat.STATX_BTIME`.
29193017

29203018
.. note::
29213019

29223020
The exact meaning and resolution of the :attr:`st_atime`,
2923-
:attr:`st_mtime`, and :attr:`st_ctime` attributes depend on the operating
2924-
system and the file system. For example, on Windows systems using the FAT
2925-
or FAT32 file systems, :attr:`st_mtime` has 2-second resolution, and
2926-
:attr:`st_atime` has only 1-day resolution. See your operating system
2927-
documentation for details.
3021+
:attr:`st_mtime`, :attr:`st_ctime` and :attr:`st_birthtime` attributes
3022+
depend on the operating system and the file system. For example, on
3023+
Windows systems using the FAT or FAT32 file systems, :attr:`st_mtime` has
3024+
2-second resolution, and :attr:`st_atime` has only 1-day resolution.
3025+
See your operating system documentation for details.
29283026

29293027
Similarly, although :attr:`st_atime_ns`, :attr:`st_mtime_ns`,
29303028
and :attr:`st_ctime_ns` are always expressed in nanoseconds, many
@@ -2943,11 +3041,15 @@ features:
29433041
Number of 512-byte blocks allocated for file.
29443042
This may be smaller than :attr:`st_size`/512 when the file has holes.
29453043

3044+
This field is set with :data:`stat.STATX_BLOCKS`.
3045+
29463046
.. attribute:: st_blksize
29473047

29483048
"Preferred" blocksize for efficient file system I/O. Writing to a file in
29493049
smaller chunks may cause an inefficient read-modify-rewrite.
29503050

3051+
This field is set with :data:`stat.STATX_BLOCKSIZE`.
3052+
29513053
.. attribute:: st_rdev
29523054

29533055
Type of device if an inode device.
@@ -2956,17 +3058,27 @@ features:
29563058

29573059
User defined flags for file.
29583060

3061+
.. attribute:: stx_attributes
3062+
3063+
Additional attribute flags (``STATX_ATTR_*`` values).
3064+
3065+
This field is only set for calls using :func:`os.statx`.
3066+
3067+
.. attribute:: stx_attributes_mask
3068+
3069+
Attribute flags (``STATX_ATTR_*``) that were supported on the file system
3070+
containing the file. Flags not set in this mask are meaningless in
3071+
:attr:`stx_attributes`.
3072+
3073+
This field is only set for calls using :func:`os.statx`.
3074+
29593075
On other Unix systems (such as FreeBSD), the following attributes may be
29603076
available (but may be only filled out if root tries to use them):
29613077

29623078
.. attribute:: st_gen
29633079

29643080
File generation number.
29653081

2966-
.. attribute:: st_birthtime
2967-
2968-
Time of file creation.
2969-
29703082
On Solaris and derivatives, the following attributes may also be
29713083
available:
29723084

@@ -2998,12 +3110,16 @@ features:
29983110
:c:func:`GetFileInformationByHandle`. See the ``FILE_ATTRIBUTE_*``
29993111
constants in the :mod:`stat` module.
30003112

3113+
This field is requested with :data:`stat.STATX_TYPE`.
3114+
30013115
.. attribute:: st_reparse_tag
30023116

30033117
When :attr:`st_file_attributes` has the ``FILE_ATTRIBUTE_REPARSE_POINT``
30043118
set, this field contains the tag identifying the type of reparse point.
30053119
See the ``IO_REPARSE_TAG_*`` constants in the :mod:`stat` module.
30063120

3121+
This field is requested with :data:`stat.STATX_TYPE`.
3122+
30073123
The standard module :mod:`stat` defines functions and constants that are
30083124
useful for extracting information from a :c:type:`stat` structure. (On
30093125
Windows, some items are filled with dummy values.)
@@ -3039,6 +3155,13 @@ features:
30393155
files as :const:`S_IFCHR`, :const:`S_IFIFO` or :const:`S_IFBLK`
30403156
as appropriate.
30413157

3158+
.. versionchanged:: 3.12
3159+
Added the :attr:`stx_mask` member along with :func:`statx`.
3160+
3161+
.. versionchanged:: 3.12
3162+
Added the :attr:`st_birthtime` member on Windows.
3163+
3164+
30423165
.. function:: statvfs(path)
30433166

30443167
Perform a :c:func:`statvfs` system call on the given path. The return value is

Include/internal/pycore_fileutils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ struct _Py_stat_struct {
8282
int st_ctime_nsec;
8383
unsigned long st_file_attributes;
8484
unsigned long st_reparse_tag;
85+
time_t st_btime;
86+
int st_btime_nsec;
8587
};
8688
#else
8789
# define _Py_stat_struct stat
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#ifndef Py_INTERNAL_FILEUTILS_WINDOWS_H
2+
#define Py_INTERNAL_FILEUTILS_WINDOWS_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "Py_BUILD_CORE must be defined to include this header"
9+
#endif
10+
11+
#ifdef MS_WINDOWS
12+
13+
#if !defined(NTDDI_WIN10_NI) || !(NTDDI_VERSION >= NTDDI_WIN10_NI)
14+
typedef struct _FILE_STAT_BASIC_INFORMATION {
15+
LARGE_INTEGER FileId;
16+
LARGE_INTEGER CreationTime;
17+
LARGE_INTEGER LastAccessTime;
18+
LARGE_INTEGER LastWriteTime;
19+
LARGE_INTEGER ChangeTime;
20+
LARGE_INTEGER AllocationSize;
21+
LARGE_INTEGER EndOfFile;
22+
ULONG FileAttributes;
23+
ULONG ReparseTag;
24+
ULONG NumberOfLinks;
25+
ULONG DeviceType;
26+
ULONG DeviceCharacteristics;
27+
} FILE_STAT_BASIC_INFORMATION;
28+
29+
typedef enum _FILE_INFO_BY_NAME_CLASS {
30+
FileStatByNameInfo,
31+
FileStatLxByNameInfo,
32+
FileCaseSensitiveByNameInfo,
33+
FileStatBasicByNameInfo,
34+
MaximumFileInfoByNameClass
35+
} FILE_INFO_BY_NAME_CLASS;
36+
#endif
37+
38+
typedef BOOL (WINAPI *PGetFileInformationByName)(
39+
PCWSTR FileName,
40+
FILE_INFO_BY_NAME_CLASS FileInformationClass,
41+
PVOID FileInfoBuffer,
42+
ULONG FileInfoBufferSize
43+
);
44+
45+
static inline BOOL GetFileInformationByName(
46+
PCWSTR FileName,
47+
FILE_INFO_BY_NAME_CLASS FileInformationClass,
48+
PVOID FileInfoBuffer,
49+
ULONG FileInfoBufferSize
50+
) {
51+
static PGetFileInformationByName GetFileInformationByName = NULL;
52+
static int GetFileInformationByName_init = -1;
53+
54+
if (GetFileInformationByName_init < 0) {
55+
HMODULE hMod = LoadLibraryW(L"api-ms-win-core-file-l2-1-4");
56+
GetFileInformationByName_init = 0;
57+
if (hMod) {
58+
GetFileInformationByName = (PGetFileInformationByName)GetProcAddress(
59+
hMod, "GetFileInformationByName");
60+
if (GetFileInformationByName) {
61+
GetFileInformationByName_init = 1;
62+
} else {
63+
FreeLibrary(hMod);
64+
}
65+
}
66+
}
67+
68+
if (GetFileInformationByName_init <= 0) {
69+
SetLastError(ERROR_NOT_SUPPORTED);
70+
return FALSE;
71+
}
72+
return GetFileInformationByName(FileName, FileInformationClass, FileInfoBuffer, FileInfoBufferSize);
73+
}
74+
75+
#endif
76+
77+
#endif

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ struct _Py_global_strings {
495495
STRUCT_FOR_ID(logoption)
496496
STRUCT_FOR_ID(loop)
497497
STRUCT_FOR_ID(mapping)
498+
STRUCT_FOR_ID(mask)
498499
STRUCT_FOR_ID(match)
499500
STRUCT_FOR_ID(max_length)
500501
STRUCT_FOR_ID(maxdigits)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)