Added the :attr:`st_birthtime` member on Windows.
+.. function:: statx(path, mask, *, flags=0, dir_fd=None, follow_symlinks=True)
+
+ Get the status of a file or file descriptor by performing a :c:func:`!statx`
+ system call on the given path.
+
+ *path* is a :term:`path-like object` or an open file descriptor. *mask* is a
+ combination of the module-level :const:`STATX_* <STATX_TYPE>` constants
+ specifying the information to retrieve. *flags* is a combination of the
+ module-level :const:`AT_STATX_* <AT_STATX_FORCE_SYNC>` constants and/or
+ :const:`AT_NO_AUTOMOUNT`. Returns a :class:`statx_result` object whose
+ :attr:`~os.statx_result.stx_mask` attribute specifies the information
+ actually retrieved (which may differ from *mask*).
+
+ This function supports :ref:`specifying a file descriptor <path_fd>`,
+ :ref:`paths relative to directory descriptors <dir_fd>`, and
+ :ref:`not following symlinks <follow_symlinks>`.
+
+ .. seealso:: The :manpage:`statx(2)` man page.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28.
+
+ .. versionadded:: next
+
+
+.. class:: statx_result
+
+ Information about a file returned by :func:`os.statx`.
+
+ :class:`!statx_result` has all the attributes that :class:`~stat_result` has
+ on Linux, making it :term:`duck-typing` compatible, but
+ :class:`!statx_result` is not a subclass of :class:`~stat_result` and cannot
+ be used as a tuple.
+
+ :class:`!statx_result` has the following additional attributes:
+
+ .. attribute:: stx_mask
+
+ Bitmask of :const:`STATX_* <STATX_TYPE>` constants specifying the
+ information retrieved, which may differ from what was requested.
+
+ .. attribute:: stx_attributes_mask
+
+ Bitmask of :const:`STATX_ATTR_* <stat.STATX_ATTR_COMPRESSED>` constants
+ specifying the attributes bits supported for this file.
+
+ .. attribute:: stx_attributes
+
+ Bitmask of :const:`STATX_ATTR_* <stat.STATX_ATTR_COMPRESSED>` constants
+ specifying the attributes of this file.
+
+ .. attribute:: stx_dev_major
+
+ Major number of the device on which this file resides.
+
+ .. attribute:: stx_dev_minor
+
+ Minor number of the device on which this file resides.
+
+ .. attribute:: stx_rdev_major
+
+ Major number of the device this file represents.
+
+ .. attribute:: stx_rdev_minor
+
+ Minor number of the device this file represents.
+
+ .. attribute:: stx_mnt_id
+
+ Mount ID.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 5.8.
+
+ .. attribute:: stx_dio_mem_align
+
+ Direct I/O memory buffer alignment requirement.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.1.
+
+ .. attribute:: stx_dio_offset_align
+
+ Direct I/O file offset alignment requirement.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.1.
+
+ .. attribute:: stx_subvol
+
+ Subvolume ID.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.10.
+
+ .. attribute:: stx_atomic_write_unit_min
+
+ Minimum size for direct I/O with torn-write protection.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.11.
+
+ .. attribute:: stx_atomic_write_unit_max
+
+ Maximum size for direct I/O with torn-write protection.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.11.
+
+ .. attribute:: stx_atomic_write_unit_max_opt
+
+ Maximum optimized size for direct I/O with torn-write protection.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.11.
+
+ .. attribute:: stx_atomic_write_segments_max
+
+ Maximum iovecs for direct I/O with torn-write protection.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.11.
+
+ .. attribute:: stx_dio_read_offset_align
+
+ Direct I/O file offset alignment requirement for reads.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
+ userspace API headers >= 6.14.
+
+ .. seealso:: The :manpage:`statx(2)` man page.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28.
+
+ .. versionadded:: next
+
+
+.. data:: STATX_TYPE
+ STATX_MODE
+ STATX_NLINK
+ STATX_UID
+ STATX_GID
+ STATX_ATIME
+ STATX_MTIME
+ STATX_CTIME
+ STATX_INO
+ STATX_SIZE
+ STATX_BLOCKS
+ STATX_BASIC_STATS
+ STATX_BTIME
+ STATX_MNT_ID
+ STATX_DIOALIGN
+ STATX_MNT_ID_UNIQUE
+ STATX_SUBVOL
+ STATX_WRITE_ATOMIC
+ STATX_DIO_READ_ALIGN
+
+ Bitflags for use in the *mask* parameter to :func:`os.statx`. Flags
+ including and after :const:`!STATX_MNT_ID` are only available when their
+ corresponding members in :class:`statx_result` are available.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28.
+
+ .. versionadded:: next
+
+.. data:: AT_STATX_FORCE_SYNC
+
+ A flag for the :func:`os.statx` function. Requests that the kernel return
+ up-to-date information even when doing so is expensive (for example,
+ requiring a round trip to the server for a file on a network filesystem).
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28.
+
+ .. versionadded:: next
+
+.. data:: AT_STATX_DONT_SYNC
+
+ A flag for the :func:`os.statx` function. Requests that the kernel return
+ cached information if possible.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28.
+
+ .. versionadded:: next
+
+.. data:: AT_STATX_SYNC_AS_STAT
+
+ A flag for the :func:`os.statx` function. This flag is defined as ``0``, so
+ it has no effect, but it can be used to explicitly indicate neither
+ :data:`AT_STATX_FORCE_SYNC` nor :data:`AT_STATX_DONT_SYNC` is being passed.
+ In the absence of the other two flags, the kernel will generally return
+ information as fresh as :func:`os.stat` would return.
+
+ .. availability:: Linux >= 4.11 with glibc >= 2.28.
+
+ .. versionadded:: next
+
+
+.. data:: AT_NO_AUTOMOUNT
+
+ If the final component of a path is an automount point, operate on the
+ automount point instead of performing the automount. On Linux,
+ :func:`os.stat`, :func:`os.fstat` and :func:`os.lstat` always behave this
+ way.
+
+ .. availability:: Linux.
+
+ .. versionadded:: next
+
+
.. function:: statvfs(path)
Perform a :c:func:`!statvfs` system call on the given path. The return value is
IO_REPARSE_TAG_APPEXECLINK
.. versionadded:: 3.8
+
+On Linux, the following file attribute constants are available for use when
+testing bits in the :attr:`~os.statx_result.stx_attributes` and
+:attr:`~os.statx_result.stx_attributes_mask` members returned by
+:func:`os.statx`. See the :manpage:`statx(2)` man page for more detail on the
+meaning of these constants.
+
+.. data:: STATX_ATTR_COMPRESSED
+ STATX_ATTR_IMMUTABLE
+ STATX_ATTR_APPEND
+ STATX_ATTR_NODUMP
+ STATX_ATTR_ENCRYPTED
+ STATX_ATTR_AUTOMOUNT
+ STATX_ATTR_MOUNT_ROOT
+ STATX_ATTR_VERITY
+ STATX_ATTR_DAX
+ STATX_ATTR_WRITE_ATOMIC
+
+ .. versionadded:: next
(Contributed by Serhiy Storchaka in :gh:`78502`.)
+os
+--
+
+* Add :func:`os.statx` on Linux kernel versions 4.11 and later with
+ glibc versions 2.28 and later.
+ (Contributed by Jeffrey Bosboom in :gh:`83714`.)
+
+
os.path
-------
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(loop));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(manual_reset));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mapping));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mask));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(match));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(max_length));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(maxdigits));
STRUCT_FOR_ID(loop)
STRUCT_FOR_ID(manual_reset)
STRUCT_FOR_ID(mapping)
+ STRUCT_FOR_ID(mask)
STRUCT_FOR_ID(match)
STRUCT_FOR_ID(max_length)
STRUCT_FOR_ID(maxdigits)
INIT_ID(loop), \
INIT_ID(manual_reset), \
INIT_ID(mapping), \
+ INIT_ID(mask), \
INIT_ID(match), \
INIT_ID(max_length), \
INIT_ID(maxdigits), \
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
+ string = &_Py_ID(mask);
+ _PyUnicode_InternStatic(interp, &string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(match);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
_add("HAVE_UNLINKAT", "unlink")
_add("HAVE_UNLINKAT", "rmdir")
_add("HAVE_UTIMENSAT", "utime")
+ if _exists("statx"):
+ _set.add(statx)
supports_dir_fd = _set
_set = set()
_add("HAVE_FPATHCONF", "pathconf")
if _exists("statvfs") and _exists("fstatvfs"): # mac os x10.3
_add("HAVE_FSTATVFS", "statvfs")
+ if _exists("statx"):
+ _set.add(statx)
supports_fd = _set
_set = set()
_add("HAVE_FSTATAT", "stat")
_add("HAVE_UTIMENSAT", "utime")
_add("MS_WINDOWS", "stat")
+ if _exists("statx"):
+ _set.add(statx)
supports_follow_symlinks = _set
del _set
FILE_ATTRIBUTE_VIRTUAL = 65536
+# Linux STATX_ATTR constants for interpreting os.statx()'s
+# "stx_attributes" and "stx_attributes_mask" members
+
+STATX_ATTR_COMPRESSED = 0x00000004
+STATX_ATTR_IMMUTABLE = 0x00000010
+STATX_ATTR_APPEND = 0x00000020
+STATX_ATTR_NODUMP = 0x00000040
+STATX_ATTR_ENCRYPTED = 0x00000800
+STATX_ATTR_AUTOMOUNT = 0x00001000
+STATX_ATTR_MOUNT_ROOT = 0x00002000
+STATX_ATTR_VERITY = 0x00100000
+STATX_ATTR_DAX = 0x00200000
+STATX_ATTR_WRITE_ATOMIC = 0x00400000
+
+
# If available, use C implementation
try:
from _stat import *
self.addCleanup(os_helper.unlink, self.fname)
create_file(self.fname, b"ABC")
+ def check_timestamp_agreement(self, result, names):
+ # Make sure that the st_?time and st_?time_ns fields roughly agree
+ # (they should always agree up to around tens-of-microseconds)
+ for name in names:
+ floaty = int(getattr(result, name) * 100_000)
+ nanosecondy = getattr(result, name + "_ns") // 10_000
+ self.assertAlmostEqual(floaty, nanosecondy, delta=2, msg=name)
+
def check_stat_attributes(self, fname):
result = os.stat(fname)
result[getattr(stat, name)])
self.assertIn(attr, members)
- # Make sure that the st_?time and st_?time_ns fields roughly agree
- # (they should always agree up to around tens-of-microseconds)
- for name in 'st_atime st_mtime st_ctime'.split():
- floaty = int(getattr(result, name) * 100000)
- nanosecondy = getattr(result, name + "_ns") // 10000
- self.assertAlmostEqual(floaty, nanosecondy, delta=2)
-
- # Ensure both birthtime and birthtime_ns roughly agree, if present
+ time_attributes = ['st_atime', 'st_mtime', 'st_ctime']
try:
- floaty = int(result.st_birthtime * 100000)
- nanosecondy = result.st_birthtime_ns // 10000
+ result.st_birthtime
+ result.st_birthtime_ns
except AttributeError:
pass
else:
- self.assertAlmostEqual(floaty, nanosecondy, delta=2)
+ time_attributes.append('st_birthtime')
+ self.check_timestamp_agreement(result, time_attributes)
try:
result[200]
unpickled = pickle.loads(p)
self.assertEqual(result, unpickled)
+ def check_statx_attributes(self, fname):
+ maximal_mask = 0
+ for name in dir(os):
+ if name.startswith('STATX_'):
+ maximal_mask |= getattr(os, name)
+ result = os.statx(self.fname, maximal_mask)
+
+ time_attributes = ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime')
+ self.check_timestamp_agreement(result, time_attributes)
+
+ # Check that valid attributes match os.stat.
+ requirements = (
+ ('st_mode', os.STATX_TYPE | os.STATX_MODE),
+ ('st_nlink', os.STATX_NLINK),
+ ('st_uid', os.STATX_UID),
+ ('st_gid', os.STATX_GID),
+ ('st_atime', os.STATX_ATIME),
+ ('st_atime_ns', os.STATX_ATIME),
+ ('st_mtime', os.STATX_MTIME),
+ ('st_mtime_ns', os.STATX_MTIME),
+ ('st_ctime', os.STATX_CTIME),
+ ('st_ctime_ns', os.STATX_CTIME),
+ ('st_ino', os.STATX_INO),
+ ('st_size', os.STATX_SIZE),
+ ('st_blocks', os.STATX_BLOCKS),
+ ('st_birthtime', os.STATX_BTIME),
+ ('st_birthtime_ns', os.STATX_BTIME),
+ # unconditionally valid members
+ ('st_blksize', 0),
+ ('st_rdev', 0),
+ ('st_dev', 0),
+ )
+ basic_result = os.stat(self.fname)
+ for name, bits in requirements:
+ if result.stx_mask & bits == bits and hasattr(basic_result, name):
+ x = getattr(result, name)
+ b = getattr(basic_result, name)
+ self.assertEqual(type(x), type(b))
+ if isinstance(x, float):
+ self.assertAlmostEqual(x, b, msg=name)
+ else:
+ self.assertEqual(x, b, msg=name)
+
+ self.assertEqual(result.stx_rdev_major, os.major(result.st_rdev))
+ self.assertEqual(result.stx_rdev_minor, os.minor(result.st_rdev))
+ self.assertEqual(result.stx_dev_major, os.major(result.st_dev))
+ self.assertEqual(result.stx_dev_minor, os.minor(result.st_dev))
+
+ members = [name for name in dir(result)
+ if name.startswith('st_') or name.startswith('stx_')]
+ for name in members:
+ try:
+ setattr(result, name, 1)
+ self.fail("No exception raised")
+ except AttributeError:
+ pass
+
+ self.assertEqual(result.stx_attributes & result.stx_attributes_mask,
+ result.stx_attributes)
+
+ # statx_result is not a tuple or tuple-like object.
+ with self.assertRaisesRegex(TypeError, 'not subscriptable'):
+ result[0]
+ with self.assertRaisesRegex(TypeError, 'cannot unpack'):
+ _, _ = result
+
+ @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()')
+ def test_statx_attributes(self):
+ self.check_statx_attributes(self.fname)
+
+ @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()')
+ def test_statx_attributes_bytes(self):
+ try:
+ fname = self.fname.encode(sys.getfilesystemencoding())
+ except UnicodeEncodeError:
+ self.skipTest("cannot encode %a for the filesystem" % self.fname)
+ self.check_statx_attributes(fname)
+
+ @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()')
+ def test_statx_attributes_pathlike(self):
+ self.check_statx_attributes(FakePath(self.fname))
+
@unittest.skipUnless(hasattr(os, 'statvfs'), 'test needs os.statvfs()')
def test_statvfs_attributes(self):
result = os.statvfs(self.fname)
finally:
fp.close()
- def test_stat(self):
- self.assertTrue(posix.stat(os_helper.TESTFN))
- self.assertTrue(posix.stat(os.fsencode(os_helper.TESTFN)))
+ def check_statlike_path(self, func):
+ self.assertTrue(func(os_helper.TESTFN))
+ self.assertTrue(func(os.fsencode(os_helper.TESTFN)))
+ self.assertTrue(func(os_helper.FakePath(os_helper.TESTFN)))
self.assertRaisesRegex(TypeError,
'should be string, bytes, os.PathLike or integer, not',
- posix.stat, bytearray(os.fsencode(os_helper.TESTFN)))
+ func, bytearray(os.fsencode(os_helper.TESTFN)))
self.assertRaisesRegex(TypeError,
'should be string, bytes, os.PathLike or integer, not',
- posix.stat, None)
+ func, None)
self.assertRaisesRegex(TypeError,
'should be string, bytes, os.PathLike or integer, not',
- posix.stat, list(os_helper.TESTFN))
+ func, list(os_helper.TESTFN))
self.assertRaisesRegex(TypeError,
'should be string, bytes, os.PathLike or integer, not',
- posix.stat, list(os.fsencode(os_helper.TESTFN)))
+ func, list(os.fsencode(os_helper.TESTFN)))
+
+ def test_stat(self):
+ self.check_statlike_path(posix.stat)
+
+ @unittest.skipUnless(hasattr(posix, 'statx'), 'test needs posix.statx()')
+ def test_statx(self):
+ def func(path, **kwargs):
+ return posix.statx(path, posix.STATX_BASIC_STATS, **kwargs)
+ self.check_statlike_path(func)
+
+ @unittest.skipUnless(hasattr(posix, 'statx'), 'test needs posix.statx()')
+ def test_statx_flags(self):
+ # glibc's fallback implementation of statx via the stat family fails
+ # with EINVAL on the (nonzero) sync flags. If you see this failure,
+ # update your kernel and/or seccomp syscall filter.
+ valid_flag_names = ('AT_NO_AUTOMOUNT', 'AT_STATX_SYNC_AS_STAT',
+ 'AT_STATX_FORCE_SYNC', 'AT_STATX_DONT_SYNC')
+ for flag_name in valid_flag_names:
+ flag = getattr(posix, flag_name)
+ with self.subTest(msg=flag_name, flags=flag):
+ posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS,
+ flags=flag)
+
+ # These flags are not exposed to Python because their functionality is
+ # implemented via kwargs instead.
+ kwarg_equivalent_flags = (
+ (0x0100, 'AT_SYMLINK_NOFOLLOW', 'follow_symlinks'),
+ (0x0400, 'AT_SYMLINK_FOLLOW', 'follow_symlinks'),
+ (0x1000, 'AT_EMPTY_PATH', 'dir_fd'),
+ )
+ for flag, flag_name, kwarg_name in kwarg_equivalent_flags:
+ with self.subTest(msg=flag_name, flags=flag):
+ with self.assertRaisesRegex(ValueError, kwarg_name):
+ posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS,
+ flags=flag)
+
+ with self.subTest(msg="AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC"):
+ with self.assertRaises(OSError) as ctx:
+ flags = posix.AT_STATX_FORCE_SYNC | posix.AT_STATX_DONT_SYNC
+ posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS,
+ flags=flags)
+ self.assertEqual(ctx.exception.errno, errno.EINVAL)
@unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()")
def test_mkfifo(self):
with self.prepare_file() as (dir_fd, name, fullname):
posix.chown(name, os.getuid(), os.getgid(), dir_fd=dir_fd)
- @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()")
- def test_stat_dir_fd(self):
+ def check_statlike_dir_fd(self, func):
with self.prepare() as (dir_fd, name, fullname):
with open(fullname, 'w') as outfile:
outfile.write("testline\n")
self.addCleanup(posix.unlink, fullname)
- s1 = posix.stat(fullname)
- s2 = posix.stat(name, dir_fd=dir_fd)
- self.assertEqual(s1, s2)
- s2 = posix.stat(fullname, dir_fd=None)
- self.assertEqual(s1, s2)
+ s1 = func(fullname)
+ s2 = func(name, dir_fd=dir_fd)
+ self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino))
+ s2 = func(fullname, dir_fd=None)
+ self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino))
self.assertRaisesRegex(TypeError, 'should be integer or None, not',
- posix.stat, name, dir_fd=posix.getcwd())
+ func, name, dir_fd=posix.getcwd())
self.assertRaisesRegex(TypeError, 'should be integer or None, not',
- posix.stat, name, dir_fd=float(dir_fd))
+ func, name, dir_fd=float(dir_fd))
self.assertRaises(OverflowError,
- posix.stat, name, dir_fd=10**20)
+ func, name, dir_fd=10**20)
for fd in False, True:
with self.assertWarnsRegex(RuntimeWarning,
'bool is used as a file descriptor') as cm:
with self.assertRaises(OSError):
- posix.stat('nonexisting', dir_fd=fd)
+ func('nonexisting', dir_fd=fd)
self.assertEqual(cm.filename, __file__)
+ @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()")
+ def test_stat_dir_fd(self):
+ self.check_statlike_dir_fd(posix.stat)
+
+ @unittest.skipUnless(hasattr(posix, 'statx'), "test needs os.statx()")
+ def test_statx_dir_fd(self):
+ def func(path, **kwargs):
+ return posix.statx(path, os.STATX_INO, **kwargs)
+ self.check_statlike_dir_fd(func)
+
@unittest.skipUnless(os.utime in os.supports_dir_fd, "test needs dir_fd support in os.utime()")
def test_utime_dir_fd(self):
with self.prepare_file() as (dir_fd, name, fullname):
Matias Bordese
Jonas Borgström
Jurjen Bos
+Jeffrey Bosboom
Peter Bosch
Dan Boswell
Eric Bouck
--- /dev/null
+Implement :func:`os.statx` on Linux kernel versions 4.11 and later with
+glibc versions 2.28 and later. Contributed by Jeffrey Bosboom.
return return_value;
}
+#if defined(HAVE_STATX)
+
+PyDoc_STRVAR(os_statx__doc__,
+"statx($module, /, path, mask, *, flags=0, dir_fd=None,\n"
+" follow_symlinks=True)\n"
+"--\n"
+"\n"
+"Perform a statx system call on the given path.\n"
+"\n"
+" path\n"
+" Path to be examined; can be string, bytes, a path-like object or\n"
+" open-file-descriptor int.\n"
+" mask\n"
+" A bitmask of STATX_* constants defining the requested information.\n"
+" flags\n"
+" A bitmask of AT_NO_AUTOMOUNT and/or AT_STATX_* flags.\n"
+" dir_fd\n"
+" If not None, it should be a file descriptor open to a directory,\n"
+" and path should be a relative string; path will then be relative to\n"
+" that directory.\n"
+" follow_symlinks\n"
+" If False, and the last element of the path is a symbolic link,\n"
+" statx will examine the symbolic link itself instead of the file\n"
+" the link points to.\n"
+"\n"
+"It\'s an error to use dir_fd or follow_symlinks when specifying path as\n"
+" an open file descriptor.");
+
+#define OS_STATX_METHODDEF \
+ {"statx", _PyCFunction_CAST(os_statx), METH_FASTCALL|METH_KEYWORDS, os_statx__doc__},
+
+static PyObject *
+os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags,
+ int dir_fd, int follow_symlinks);
+
+static PyObject *
+os_statx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 5
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ Py_hash_t ob_hash;
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_hash = -1,
+ .ob_item = { &_Py_ID(path), &_Py_ID(mask), &_Py_ID(flags), &_Py_ID(dir_fd), &_Py_ID(follow_symlinks), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"path", "mask", "flags", "dir_fd", "follow_symlinks", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "statx",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[5];
+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2;
+ path_t path = PATH_T_INITIALIZE_P("statx", "path", 0, 0, 0, 1);
+ unsigned int mask;
+ int flags = 0;
+ int dir_fd = DEFAULT_DIR_FD;
+ int follow_symlinks = 1;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
+ /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!path_converter(args[0], &path)) {
+ goto exit;
+ }
+ {
+ Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &mask, sizeof(unsigned int),
+ Py_ASNATIVEBYTES_NATIVE_ENDIAN |
+ Py_ASNATIVEBYTES_ALLOW_INDEX |
+ Py_ASNATIVEBYTES_UNSIGNED_BUFFER);
+ if (_bytes < 0) {
+ goto exit;
+ }
+ if ((size_t)_bytes > sizeof(unsigned int)) {
+ if (PyErr_WarnEx(PyExc_DeprecationWarning,
+ "integer value out of range", 1) < 0)
+ {
+ goto exit;
+ }
+ }
+ }
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ if (args[2]) {
+ flags = PyLong_AsInt(args[2]);
+ if (flags == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (args[3]) {
+ if (!dir_fd_converter(args[3], &dir_fd)) {
+ goto exit;
+ }
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ follow_symlinks = PyObject_IsTrue(args[4]);
+ if (follow_symlinks < 0) {
+ goto exit;
+ }
+skip_optional_kwonly:
+ return_value = os_statx_impl(module, &path, mask, flags, dir_fd, follow_symlinks);
+
+exit:
+ /* Cleanup for path */
+ path_cleanup(&path);
+
+ return return_value;
+}
+
+#endif /* defined(HAVE_STATX) */
+
PyDoc_STRVAR(os_access__doc__,
"access($module, /, path, mode, *, dir_fd=None, effective_ids=False,\n"
" follow_symlinks=True)\n"
#endif /* defined(__EMSCRIPTEN__) */
+#ifndef OS_STATX_METHODDEF
+ #define OS_STATX_METHODDEF
+#endif /* !defined(OS_STATX_METHODDEF) */
+
#ifndef OS_TTYNAME_METHODDEF
#define OS_TTYNAME_METHODDEF
#endif /* !defined(OS_TTYNAME_METHODDEF) */
#ifndef OS__EMSCRIPTEN_LOG_METHODDEF
#define OS__EMSCRIPTEN_LOG_METHODDEF
#endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */
-/*[clinic end generated code: output=67f0df7cd5a7de20 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=44f7a1a16dad2e08 input=a9049054013a1b77]*/
// --- System includes ------------------------------------------------------
+#include <stddef.h> // offsetof()
#include <stdio.h> // ctermid()
#include <stdlib.h> // system()
# define STRUCT_STAT struct stat
#endif
+#ifdef HAVE_STATX
+/* until we can assume glibc 2.28 at runtime, we must weakly link */
+# pragma weak statx
+static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
+#ifdef STATX_MNT_ID
+ | STATX_MNT_ID
+#endif
+#ifdef STATX_DIOALIGN
+ | STATX_DIOALIGN
+#endif
+#ifdef STATX_MNT_ID_UNIQUE
+ | STATX_MNT_ID_UNIQUE
+#endif
+#ifdef STATX_SUBVOL
+ | STATX_SUBVOL
+#endif
+#ifdef STATX_WRITE_ATOMIC
+ | STATX_WRITE_ATOMIC
+#endif
+#ifdef STATX_DIO_READ_ALIGN
+ | STATX_DIO_READ_ALIGN
+#endif
+ );
+#endif /* HAVE_STATX */
+
#if !defined(EX_OK) && defined(EXIT_SUCCESS)
# define EX_OK EXIT_SUCCESS
#endif
newfunc statresult_new_orig;
PyObject *StatResultType;
+#ifdef HAVE_STATX
+ PyObject *StatxResultType;
+#endif
PyObject *StatVFSResultType;
PyObject *TerminalSizeType;
PyObject *TimesResultType;
Py_CLEAR(state->SchedParamType);
#endif
Py_CLEAR(state->StatResultType);
+#ifdef HAVE_STATX
+ Py_CLEAR(state->StatxResultType);
+#endif
Py_CLEAR(state->StatVFSResultType);
Py_CLEAR(state->TerminalSizeType);
Py_CLEAR(state->TimesResultType);
Py_VISIT(state->SchedParamType);
#endif
Py_VISIT(state->StatResultType);
+#ifdef HAVE_STATX
+ Py_VISIT(state->StatxResultType);
+#endif
Py_VISIT(state->StatVFSResultType);
Py_VISIT(state->TerminalSizeType);
Py_VISIT(state->TimesResultType);
_posix_clear((PyObject *)module);
}
+
+#define SEC_TO_NS (1000000000LL)
+static PyObject *
+stat_nanosecond_timestamp(_posixstate *state, time_t sec, unsigned long nsec)
+{
+ /* 1677-09-21 00:12:44 to 2262-04-11 23:47:15 UTC inclusive */
+ if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) {
+ return PyLong_FromLongLong(sec * SEC_TO_NS + nsec);
+ }
+ else {
+ PyObject *ns_total = NULL;
+ PyObject *s_in_ns = NULL;
+ PyObject *s = _PyLong_FromTime_t(sec);
+ PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
+ if (s == NULL || ns_fractional == NULL) {
+ goto exit;
+ }
+
+ s_in_ns = PyNumber_Multiply(s, state->billion);
+ if (s_in_ns == NULL) {
+ goto exit;
+ }
+
+ ns_total = PyNumber_Add(s_in_ns, ns_fractional);
+
+ exit:
+ Py_XDECREF(s);
+ Py_XDECREF(ns_fractional);
+ Py_XDECREF(s_in_ns);
+ return ns_total;
+ }
+}
+
static int
fill_time(_posixstate *state, PyObject *v, int s_index, int f_index,
int ns_index, time_t sec, unsigned long nsec)
{
assert(!PyErr_Occurred());
-#define SEC_TO_NS (1000000000LL)
assert(nsec < SEC_TO_NS);
if (s_index >= 0) {
PyStructSequence_SET_ITEM(v, f_index, float_s);
}
- int res = -1;
if (ns_index >= 0) {
- /* 1677-09-21 00:12:44 to 2262-04-11 23:47:15 UTC inclusive */
- if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) {
- PyObject *ns_total = PyLong_FromLongLong(sec * SEC_TO_NS + nsec);
- if (ns_total == NULL) {
- return -1;
- }
- PyStructSequence_SET_ITEM(v, ns_index, ns_total);
- assert(!PyErr_Occurred());
- res = 0;
- }
- else {
- PyObject *s_in_ns = NULL;
- PyObject *ns_total = NULL;
- PyObject *s = _PyLong_FromTime_t(sec);
- PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
- if (s == NULL || ns_fractional == NULL) {
- goto exit;
- }
-
- s_in_ns = PyNumber_Multiply(s, state->billion);
- if (s_in_ns == NULL) {
- goto exit;
- }
-
- ns_total = PyNumber_Add(s_in_ns, ns_fractional);
- if (ns_total == NULL) {
- goto exit;
- }
- PyStructSequence_SET_ITEM(v, ns_index, ns_total);
- assert(!PyErr_Occurred());
- res = 0;
-
- exit:
- Py_XDECREF(s);
- Py_XDECREF(ns_fractional);
- Py_XDECREF(s_in_ns);
+ PyObject *ns_total = stat_nanosecond_timestamp(state, sec, nsec);
+ if (ns_total == NULL) {
+ return -1;
}
+ PyStructSequence_SET_ITEM(v, ns_index, ns_total);
}
- return res;
- #undef SEC_TO_NS
+ assert(!PyErr_Occurred());
+ return 0;
}
+#undef SEC_TO_NS
#ifdef MS_WINDOWS
static PyObject*
}
+#ifdef HAVE_STATX
+typedef struct {
+ PyObject_HEAD
+ double atime_sec, btime_sec, ctime_sec, mtime_sec;
+ dev_t rdev, dev;
+ struct statx stx;
+} Py_statx_result;
+
+#define M(attr, type, offset, doc) \
+ {attr, type, offset, Py_READONLY, PyDoc_STR(doc)}
+#define MM(attr, type, member, doc) \
+ M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
+#define MX(attr, type, member, doc) \
+ M(#attr, type, offsetof(Py_statx_result, member), doc)
+
+static PyMemberDef pystatx_result_members[] = {
+ MM(stx_mask, Py_T_UINT, mask, "member validity mask"),
+ MM(st_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"),
+ MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"),
+ MM(st_nlink, Py_T_UINT, nlink, "number of hard links"),
+ MM(st_uid, Py_T_UINT, uid, "user ID of owner"),
+ MM(st_gid, Py_T_UINT, gid, "group ID of owner"),
+ MM(st_mode, Py_T_USHORT, mode, "protection bits"),
+ MM(st_ino, Py_T_ULONGLONG, ino, "inode"),
+ MM(st_size, Py_T_ULONGLONG, size, "total size, in bytes"),
+ MM(st_blocks, Py_T_ULONGLONG, blocks, "number of blocks allocated"),
+ MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask,
+ "Mask of supported bits in stx_attributes"),
+ MX(st_atime, Py_T_DOUBLE, atime_sec, "time of last access"),
+ MX(st_birthtime, Py_T_DOUBLE, btime_sec, "time of creation"),
+ MX(st_ctime, Py_T_DOUBLE, ctime_sec, "time of last change"),
+ MX(st_mtime, Py_T_DOUBLE, mtime_sec, "time of last modification"),
+ MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"),
+ MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"),
+ MX(st_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"),
+ MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"),
+ MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"),
+ MX(st_dev, Py_T_ULONGLONG, dev, "device"),
+#ifdef STATX_MNT_ID
+ MM(stx_mnt_id, Py_T_ULONGLONG, mnt_id, "mount ID"),
+#endif
+#ifdef STATX_DIOALIGN
+ MM(stx_dio_mem_align, Py_T_UINT, dio_mem_align,
+ "direct I/O memory buffer alignment"),
+ MM(stx_dio_offset_align, Py_T_UINT, dio_offset_align,
+ "direct I/O file offset alignment"),
+#endif
+#ifdef STATX_SUBVOL
+ MM(stx_subvol, Py_T_ULONGLONG, subvol, "subvolume ID"),
+#endif
+#ifdef STATX_WRITE_ATOMIC
+ MM(stx_atomic_write_unit_min, Py_T_UINT, atomic_write_unit_min,
+ "minimum size for direct I/O with torn-write protection"),
+ MM(stx_atomic_write_unit_max, Py_T_UINT, atomic_write_unit_max,
+ "maximum size for direct I/O with torn-write protection"),
+ MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt,
+ "maximum optimized size for direct I/O with torn-write protection"),
+ MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max,
+ "maximum iovecs for direct I/O with torn-write protection"),
+#endif
+#ifdef STATX_DIO_READ_ALIGN
+ MM(stx_dio_read_offset_align, Py_T_UINT, dio_read_offset_align,
+ "direct I/O file offset alignment for reads"),
+#endif
+ {NULL},
+};
+
+#undef MX
+#undef MM
+#undef M
+
+static PyObject *
+pystatx_result_get_nsec(PyObject *op, void *context)
+{
+ uint16_t offset = (uintptr_t)context;
+ struct statx_timestamp *ts = (void*)op + offset;
+ _posixstate *state = PyType_GetModuleState(Py_TYPE(op));
+ assert(state != NULL);
+ return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec);
+}
+
+/* The low 16 bits of the context pointer are the offset from the start of
+ Py_statx_result to the struct statx member. */
+#define GM(attr, type, member, doc) \
+ {#attr, pystatx_result_get_##type, NULL, PyDoc_STR(doc), \
+ (void *)(offsetof(Py_statx_result, stx.stx_##member))}
+
+static PyGetSetDef pystatx_result_getset[] = {
+ GM(st_atime_ns, nsec, atime, "time of last access in nanoseconds"),
+ GM(st_birthtime_ns, nsec, btime, "time of creation in nanoseconds"),
+ GM(st_ctime_ns, nsec, ctime, "time of last change in nanoseconds"),
+ GM(st_mtime_ns, nsec, mtime, "time of last modification in nanoseconds"),
+ {NULL},
+};
+
+#undef GM
+
+static PyObject *
+pystatx_result_repr(PyObject *op)
+{
+ PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
+ if (writer == NULL) {
+ return NULL;
+ }
+#define WRITE_ASCII(s) \
+ do { \
+ if (PyUnicodeWriter_WriteASCII(writer, s, strlen(s)) < 0) { \
+ goto error; \
+ } \
+ } while (0)
+
+ WRITE_ASCII("os.statx_result(");
+
+ for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_members) - 1; ++i) {
+ if (i > 0) {
+ WRITE_ASCII(", ");
+ }
+
+ PyMemberDef *d = &pystatx_result_members[i];
+ WRITE_ASCII(d->name);
+ WRITE_ASCII("=");
+
+ PyObject *o = PyMember_GetOne((const char *)op, d);
+ if (o == NULL) {
+ goto error;
+ }
+ if (PyUnicodeWriter_WriteRepr(writer, o) < 0) {
+ Py_DECREF(o);
+ goto error;
+ }
+ Py_DECREF(o);
+ }
+
+ if (Py_ARRAY_LENGTH(pystatx_result_members) > 1
+ && Py_ARRAY_LENGTH(pystatx_result_getset) > 1) {
+ WRITE_ASCII(", ");
+ }
+
+ for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_getset) - 1; ++i) {
+ if (i > 0) {
+ WRITE_ASCII(", ");
+ }
+
+ PyGetSetDef *d = &pystatx_result_getset[i];
+ WRITE_ASCII(d->name);
+ WRITE_ASCII("=");
+
+ PyObject *o = d->get(op, d->closure);
+ if (o == NULL) {
+ goto error;
+ }
+ if (PyUnicodeWriter_WriteRepr(writer, o) < 0) {
+ Py_DECREF(o);
+ goto error;
+ }
+ Py_DECREF(o);
+ }
+
+ WRITE_ASCII(")");
+ return PyUnicodeWriter_Finish(writer);
+#undef WRITE_ASCII
+
+error:
+ PyUnicodeWriter_Discard(writer);
+ return NULL;
+}
+
+static int
+pystatx_result_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ Py_VISIT(Py_TYPE(self));
+ return 0;
+}
+
+static void
+pystatx_result_dealloc(PyObject *op)
+{
+ Py_statx_result *self = (Py_statx_result *) op;
+ PyTypeObject *tp = Py_TYPE(self);
+ PyObject_GC_UnTrack(self);
+ tp->tp_free(self);
+ Py_DECREF(tp);
+}
+
+static PyType_Slot pystatx_result_slots[] = {
+ {Py_tp_repr, pystatx_result_repr},
+ {Py_tp_traverse, pystatx_result_traverse},
+ {Py_tp_dealloc, pystatx_result_dealloc},
+ {Py_tp_members, pystatx_result_members},
+ {Py_tp_getset, pystatx_result_getset},
+ {0, NULL},
+};
+
+static PyType_Spec pystatx_result_spec = {
+ .name = "os.statx_result",
+ .basicsize = sizeof(Py_statx_result),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION,
+ .slots = pystatx_result_slots,
+};
+
+/*[clinic input]
+
+os.statx
+
+ path : path_t(allow_fd=True)
+ Path to be examined; can be string, bytes, a path-like object or
+ open-file-descriptor int.
+
+ mask: unsigned_int(bitwise=True)
+ A bitmask of STATX_* constants defining the requested information.
+
+ *
+
+ flags: int = 0
+ A bitmask of AT_NO_AUTOMOUNT and/or AT_STATX_* flags.
+
+ dir_fd : dir_fd = None
+ If not None, it should be a file descriptor open to a directory,
+ and path should be a relative string; path will then be relative to
+ that directory.
+
+ follow_symlinks: bool = True
+ If False, and the last element of the path is a symbolic link,
+ statx will examine the symbolic link itself instead of the file
+ the link points to.
+
+Perform a statx system call on the given path.
+
+It's an error to use dir_fd or follow_symlinks when specifying path as
+ an open file descriptor.
+
+[clinic start generated code]*/
+
+static PyObject *
+os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags,
+ int dir_fd, int follow_symlinks)
+/*[clinic end generated code: output=e3765979ac6fe15b input=f0116380c5dc4f2f]*/
+{
+ if (path_and_dir_fd_invalid("statx", path, dir_fd) ||
+ dir_fd_and_fd_invalid("statx", dir_fd, path->fd) ||
+ fd_and_follow_symlinks_invalid("statx", path->fd, follow_symlinks)) {
+ return NULL;
+ }
+
+ /* reject flags covered by kwargs, but allow unknown flags that may be
+ future AT_STATX_* extensions */
+ if (flags & (AT_SYMLINK_NOFOLLOW | AT_SYMLINK_FOLLOW)) {
+ PyErr_Format(PyExc_ValueError,
+ "use follow_symlinks kwarg instead of AT_SYMLINK_* flag");
+ return NULL;
+ }
+ if (flags & AT_EMPTY_PATH) {
+ PyErr_Format(PyExc_ValueError,
+ "use dir_fd kwarg instead of AT_EMPTY_PATH flag");
+ return NULL;
+ }
+
+ /* Future bits may refer to members beyond the current size of struct
+ statx, so we need to mask them off to prevent memory corruption. */
+ mask &= _Py_STATX_KNOWN;
+
+ _posixstate *state = get_posix_state(module);
+ PyTypeObject *tp = (PyTypeObject *)state->StatxResultType;
+ Py_statx_result *v = (Py_statx_result *)tp->tp_alloc(tp, 0);
+ if (v == NULL) {
+ return NULL;
+ }
+
+ int result;
+ Py_BEGIN_ALLOW_THREADS
+ if (path->fd != -1) {
+ result = statx(path->fd, "", flags | AT_EMPTY_PATH, mask, &v->stx);
+ }
+ else {
+ result = statx(dir_fd, path->narrow, flags, mask, &v->stx);
+ }
+ Py_END_ALLOW_THREADS
+
+ if (result != 0) {
+ Py_DECREF(v);
+ return path_error(path);
+ }
+
+ v->atime_sec = ((double)v->stx.stx_atime.tv_sec
+ + 1e-9 * v->stx.stx_atime.tv_nsec);
+ v->btime_sec = ((double)v->stx.stx_btime.tv_sec
+ + 1e-9 * v->stx.stx_btime.tv_nsec);
+ v->ctime_sec = ((double)v->stx.stx_ctime.tv_sec
+ + 1e-9 * v->stx.stx_ctime.tv_nsec);
+ v->mtime_sec = ((double)v->stx.stx_mtime.tv_sec
+ + 1e-9 * v->stx.stx_mtime.tv_nsec);
+ v->rdev = makedev(v->stx.stx_rdev_major, v->stx.stx_rdev_minor);
+ v->dev = makedev(v->stx.stx_dev_major, v->stx.stx_dev_minor);
+
+ assert(!PyErr_Occurred());
+ return (PyObject *)v;
+}
+#endif /* HAVE_STATX */
+
+
/*[clinic input]
os.access -> bool
static PyMethodDef posix_methods[] = {
OS_STAT_METHODDEF
+ OS_STATX_METHODDEF
OS_ACCESS_METHODDEF
OS_TTYNAME_METHODDEF
OS_CHDIR_METHODDEF
if (PyModule_Add(m, "NODEV", _PyLong_FromDev(NODEV))) return -1;
#endif
+#ifdef AT_NO_AUTOMOUNT
+ if (PyModule_AddIntMacro(m, AT_NO_AUTOMOUNT)) return -1;
+#endif
+
+#ifdef HAVE_STATX
+ if (PyModule_AddIntMacro(m, STATX_TYPE)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_MODE)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_NLINK)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_UID)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_GID)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_ATIME)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_MTIME)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_CTIME)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_INO)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_SIZE)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_BLOCKS)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_BASIC_STATS)) return -1;
+ if (PyModule_AddIntMacro(m, STATX_BTIME)) return -1;
+#ifdef STATX_MNT_ID
+ if (PyModule_AddIntMacro(m, STATX_MNT_ID)) return -1;
+#endif
+#ifdef STATX_DIOALIGN
+ if (PyModule_AddIntMacro(m, STATX_DIOALIGN)) return -1;
+#endif
+#ifdef STATX_MNT_ID_UNIQUE
+ if (PyModule_AddIntMacro(m, STATX_MNT_ID_UNIQUE)) return -1;
+#endif
+#ifdef STATX_SUBVOL
+ if (PyModule_AddIntMacro(m, STATX_SUBVOL)) return -1;
+#endif
+#ifdef STATX_WRITE_ATOMIC
+ if (PyModule_AddIntMacro(m, STATX_WRITE_ATOMIC)) return -1;
+#endif
+#ifdef STATX_DIO_READ_ALIGN
+ if (PyModule_AddIntMacro(m, STATX_DIO_READ_ALIGN)) return -1;
+#endif
+ /* STATX_ALL intentionally omitted because it is deprecated */
+ if (PyModule_AddIntMacro(m, AT_STATX_SYNC_AS_STAT)) return -1;
+ if (PyModule_AddIntMacro(m, AT_STATX_FORCE_SYNC)) return -1;
+ if (PyModule_AddIntMacro(m, AT_STATX_DONT_SYNC)) return -1;
+ /* STATX_ATTR_* constants are in the stat module */
+#endif /* HAVE_STATX */
+
#if defined(__APPLE__)
if (PyModule_AddIntConstant(m, "_COPYFILE_DATA", COPYFILE_DATA)) return -1;
if (PyModule_AddIntConstant(m, "_COPYFILE_STAT", COPYFILE_STAT)) return -1;
}
#endif
+#ifdef HAVE_STATX
+ if (statx == NULL) {
+ PyObject* dct = PyModule_GetDict(m);
+ if (dct == NULL) {
+ return -1;
+ }
+ if (PyDict_PopString(dct, "statx", NULL) < 0) {
+ return -1;
+ }
+ }
+ else {
+ state->StatxResultType = PyType_FromModuleAndSpec(m, &pystatx_result_spec, NULL);
+ if (PyModule_AddObjectRef(m, "statx_result", state->StatxResultType) < 0) {
+ return -1;
+ }
+ }
+#endif
+
/* Initialize environ dictionary */
if (PyModule_Add(m, "environ", convertenviron()) != 0) {
return -1;
then :
printf "%s\n" "#define HAVE_SPLICE 1" >>confdefs.h
+fi
+ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx"
+if test "x$ac_cv_func_statx" = xyes
+then :
+ printf "%s\n" "#define HAVE_STATX 1" >>confdefs.h
+
fi
ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime"
if test "x$ac_cv_func_strftime" = xyes
setitimer setlocale setpgid setpgrp setpriority setregid setresgid \
setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \
sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \
- sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \
+ sigwaitinfo snprintf splice statx strftime strlcpy strsignal symlinkat sync \
sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \
tmpnam tmpnam_r truncate ttyname_r umask uname unlinkat unlockpt utimensat utimes vfork \
wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \
/* Define to 1 if you have the 'statvfs' function. */
#undef HAVE_STATVFS
+/* Define to 1 if you have the 'statx' function. */
+#undef HAVE_STATX
+
/* Define if you have struct stat.st_mtim.tv_nsec */
#undef HAVE_STAT_TV_NSEC