Issue #14127: Add st_{cma}time_ns fields to os.stat() result object.
This commit is contained in:
parent
dd5aa36f17
commit
6fe20b3aee
|
@ -2011,8 +2011,8 @@ Files and Directories
|
||||||
Perform the equivalent of a :c:func:`stat` system call on the given path.
|
Perform the equivalent of a :c:func:`stat` system call on the given path.
|
||||||
(This function follows symlinks; to stat a symlink use :func:`lstat`.)
|
(This function follows symlinks; to stat a symlink use :func:`lstat`.)
|
||||||
|
|
||||||
The return value is an object whose attributes correspond to the members
|
The return value is an object whose attributes correspond roughly
|
||||||
of the :c:type:`stat` structure, namely:
|
to the members of the :c:type:`stat` structure, namely:
|
||||||
|
|
||||||
* :attr:`st_mode` - protection bits,
|
* :attr:`st_mode` - protection bits,
|
||||||
* :attr:`st_ino` - inode number,
|
* :attr:`st_ino` - inode number,
|
||||||
|
@ -2021,10 +2021,18 @@ Files and Directories
|
||||||
* :attr:`st_uid` - user id of owner,
|
* :attr:`st_uid` - user id of owner,
|
||||||
* :attr:`st_gid` - group id of owner,
|
* :attr:`st_gid` - group id of owner,
|
||||||
* :attr:`st_size` - size of file, in bytes,
|
* :attr:`st_size` - size of file, in bytes,
|
||||||
* :attr:`st_atime` - time of most recent access,
|
* :attr:`st_atime` - time of most recent access expressed in seconds,
|
||||||
* :attr:`st_mtime` - time of most recent content modification,
|
* :attr:`st_mtime` - time of most recent content modification
|
||||||
* :attr:`st_ctime` - platform dependent; time of most recent metadata change on
|
expressed in seconds,
|
||||||
Unix, or the time of creation on Windows)
|
* :attr:`st_ctime` - platform dependent; time of most recent metadata
|
||||||
|
change on Unix, or the time of creation on Windows, expressed in seconds
|
||||||
|
* :attr:`st_atime_ns` - time of most recent access
|
||||||
|
expressed in nanoseconds as an integer,
|
||||||
|
* :attr:`st_mtime_ns` - time of most recent content modification
|
||||||
|
expressed in nanoseconds as an integer,
|
||||||
|
* :attr:`st_ctime_ns` - platform dependent; time of most recent metadata
|
||||||
|
change on Unix, or the time of creation on Windows,
|
||||||
|
expressed in nanoseconds as an integer
|
||||||
|
|
||||||
On some Unix systems (such as Linux), the following attributes may also be
|
On some Unix systems (such as Linux), the following attributes may also be
|
||||||
available:
|
available:
|
||||||
|
@ -2054,6 +2062,14 @@ Files and Directories
|
||||||
or FAT32 file systems, :attr:`st_mtime` has 2-second resolution, and
|
or FAT32 file systems, :attr:`st_mtime` has 2-second resolution, and
|
||||||
:attr:`st_atime` has only 1-day resolution. See your operating system
|
:attr:`st_atime` has only 1-day resolution. See your operating system
|
||||||
documentation for details.
|
documentation for details.
|
||||||
|
Similarly, although :attr:`st_atime_ns`, :attr:`st_mtime_ns`,
|
||||||
|
and :attr:`st_ctime_ns` are always expressed in nanoseconds, many
|
||||||
|
systems do not provide nanosecond precision. On systems that do
|
||||||
|
provide nanosecond precision, the floating-point object used to
|
||||||
|
store :attr:`st_atime`, :attr:`st_mtime`, and :attr:`st_ctime`
|
||||||
|
cannot preserve all of it, and as such will be slightly inexact.
|
||||||
|
If you need the exact timestamps you should always use
|
||||||
|
:attr:`st_atime_ns`, :attr:`st_mtime_ns`, and :attr:`st_ctime_ns`.
|
||||||
|
|
||||||
For backward compatibility, the return value of :func:`~os.stat` is also accessible
|
For backward compatibility, the return value of :func:`~os.stat` is also accessible
|
||||||
as a tuple of at least 10 integers giving the most important (and portable)
|
as a tuple of at least 10 integers giving the most important (and portable)
|
||||||
|
@ -2081,6 +2097,10 @@ Files and Directories
|
||||||
|
|
||||||
Availability: Unix, Windows.
|
Availability: Unix, Windows.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
The :attr:`st_atime_ns`, :attr:`st_mtime_ns`,
|
||||||
|
and :attr:`st_ctime_ns` members.
|
||||||
|
|
||||||
|
|
||||||
.. function:: stat_float_times([newvalue])
|
.. function:: stat_float_times([newvalue])
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,10 @@ PyAPI_FUNC(int) _PyTime_ObjectToTime_t(
|
||||||
PyObject *obj,
|
PyObject *obj,
|
||||||
time_t *sec);
|
time_t *sec);
|
||||||
|
|
||||||
|
/* Convert a time_t to a PyLong. */
|
||||||
|
PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
|
||||||
|
time_t sec);
|
||||||
|
|
||||||
/* Convert a number of seconds, int or float, to a timeval structure.
|
/* Convert a number of seconds, int or float, to a timeval structure.
|
||||||
usec is in the range [0; 999999] and rounded towards zero.
|
usec is in the range [0; 999999] and rounded towards zero.
|
||||||
For example, -1.2 is converted to (-2, 800000). */
|
For example, -1.2 is converted to (-2, 800000). */
|
||||||
|
|
|
@ -191,6 +191,13 @@ class StatAttributeTests(unittest.TestCase):
|
||||||
result[getattr(stat, name)])
|
result[getattr(stat, name)])
|
||||||
self.assertIn(attr, members)
|
self.assertIn(attr, members)
|
||||||
|
|
||||||
|
# Make sure that the st_?time and st_?time_ns fields roughly agree
|
||||||
|
# (they should always agree up to the tens-of-microseconds magnitude)
|
||||||
|
for name in 'st_atime st_mtime st_ctime'.split():
|
||||||
|
floaty = int(getattr(result, name) * 100000)
|
||||||
|
nanosecondy = getattr(result, name + "_ns") // 10000
|
||||||
|
self.assertEqual(floaty, nanosecondy)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result[200]
|
result[200]
|
||||||
self.fail("No exception thrown")
|
self.fail("No exception thrown")
|
||||||
|
|
|
@ -2362,17 +2362,6 @@ run_in_subinterp(PyObject *self, PyObject *args)
|
||||||
return PyLong_FromLong(r);
|
return PyLong_FromLong(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
_PyLong_FromTime_t(time_t value)
|
|
||||||
{
|
|
||||||
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
|
|
||||||
return PyLong_FromLongLong(value);
|
|
||||||
#else
|
|
||||||
assert(sizeof(time_t) <= sizeof(long));
|
|
||||||
return PyLong_FromLong(value);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
test_pytime_object_to_time_t(PyObject *self, PyObject *args)
|
test_pytime_object_to_time_t(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1550,6 +1550,9 @@ static PyStructSequence_Field stat_result_fields[] = {
|
||||||
{"st_atime", "time of last access"},
|
{"st_atime", "time of last access"},
|
||||||
{"st_mtime", "time of last modification"},
|
{"st_mtime", "time of last modification"},
|
||||||
{"st_ctime", "time of last change"},
|
{"st_ctime", "time of last change"},
|
||||||
|
{"st_atime_ns", "time of last access in nanoseconds"},
|
||||||
|
{"st_mtime_ns", "time of last modification in nanoseconds"},
|
||||||
|
{"st_ctime_ns", "time of last change in nanoseconds"},
|
||||||
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
||||||
{"st_blksize", "blocksize for filesystem I/O"},
|
{"st_blksize", "blocksize for filesystem I/O"},
|
||||||
#endif
|
#endif
|
||||||
|
@ -1572,9 +1575,9 @@ static PyStructSequence_Field stat_result_fields[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
||||||
#define ST_BLKSIZE_IDX 13
|
#define ST_BLKSIZE_IDX 16
|
||||||
#else
|
#else
|
||||||
#define ST_BLKSIZE_IDX 12
|
#define ST_BLKSIZE_IDX 15
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
|
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||||
|
@ -1726,25 +1729,50 @@ stat_float_times(PyObject* self, PyObject *args)
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *billion = NULL;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
|
fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
|
||||||
{
|
{
|
||||||
PyObject *fval,*ival;
|
PyObject *s = _PyLong_FromTime_t(sec);
|
||||||
#if SIZEOF_TIME_T > SIZEOF_LONG
|
PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
|
||||||
ival = PyLong_FromLongLong((PY_LONG_LONG)sec);
|
PyObject *s_in_ns = NULL;
|
||||||
#else
|
PyObject *ns_total = NULL;
|
||||||
ival = PyLong_FromLong((long)sec);
|
PyObject *float_s = NULL;
|
||||||
#endif
|
|
||||||
if (!ival)
|
if (!(s && ns_fractional))
|
||||||
return;
|
goto exit;
|
||||||
|
|
||||||
|
s_in_ns = PyNumber_Multiply(s, billion);
|
||||||
|
if (!s_in_ns)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
ns_total = PyNumber_Add(s_in_ns, ns_fractional);
|
||||||
|
if (!ns_total)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
if (_stat_float_times) {
|
if (_stat_float_times) {
|
||||||
fval = PyFloat_FromDouble(sec + 1e-9*nsec);
|
float_s = PyFloat_FromDouble(sec + 1e-9*nsec);
|
||||||
} else {
|
if (!float_s)
|
||||||
fval = ival;
|
goto exit;
|
||||||
Py_INCREF(fval);
|
|
||||||
}
|
}
|
||||||
PyStructSequence_SET_ITEM(v, index, ival);
|
else {
|
||||||
PyStructSequence_SET_ITEM(v, index+3, fval);
|
float_s = s;
|
||||||
|
Py_INCREF(float_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyStructSequence_SET_ITEM(v, index, s);
|
||||||
|
PyStructSequence_SET_ITEM(v, index+3, float_s);
|
||||||
|
PyStructSequence_SET_ITEM(v, index+6, ns_total);
|
||||||
|
s = NULL;
|
||||||
|
float_s = NULL;
|
||||||
|
ns_total = NULL;
|
||||||
|
exit:
|
||||||
|
Py_XDECREF(s);
|
||||||
|
Py_XDECREF(ns_fractional);
|
||||||
|
Py_XDECREF(s_in_ns);
|
||||||
|
Py_XDECREF(ns_total);
|
||||||
|
Py_XDECREF(float_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pack a system stat C structure into the Python stat tuple
|
/* pack a system stat C structure into the Python stat tuple
|
||||||
|
@ -11627,6 +11655,10 @@ INITFUNC(void)
|
||||||
|
|
||||||
PyModule_AddObject(m, "terminal_size", (PyObject*) &TerminalSizeType);
|
PyModule_AddObject(m, "terminal_size", (PyObject*) &TerminalSizeType);
|
||||||
|
|
||||||
|
billion = PyLong_FromLong(1000000000);
|
||||||
|
if (!billion)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,17 @@ _PyLong_AsTime_t(PyObject *obj)
|
||||||
return (time_t)val;
|
return (time_t)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyLong_FromTime_t(time_t t)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
|
||||||
|
return PyLong_FromLongLong((PY_LONG_LONG)t);
|
||||||
|
#else
|
||||||
|
assert(sizeof(time_t) <= sizeof(long));
|
||||||
|
return PyLong_FromLong((long)t);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
|
_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
|
||||||
double denominator)
|
double denominator)
|
||||||
|
|
Loading…
Reference in New Issue