Issue #14127: Add st_{cma}time_ns fields to os.stat() result object.

This commit is contained in:
Larry Hastings 2012-04-19 15:07:49 -07:00
parent dd5aa36f17
commit 6fe20b3aee
6 changed files with 96 additions and 33 deletions

View File

@ -2011,8 +2011,8 @@ Files and Directories
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`.)
The return value is an object whose attributes correspond to the members
of the :c:type:`stat` structure, namely:
The return value is an object whose attributes correspond roughly
to the members of the :c:type:`stat` structure, namely:
* :attr:`st_mode` - protection bits,
* :attr:`st_ino` - inode number,
@ -2021,10 +2021,18 @@ Files and Directories
* :attr:`st_uid` - user id of owner,
* :attr:`st_gid` - group id of owner,
* :attr:`st_size` - size of file, in bytes,
* :attr:`st_atime` - time of most recent access,
* :attr:`st_mtime` - time of most recent content modification,
* :attr:`st_ctime` - platform dependent; time of most recent metadata change on
Unix, or the time of creation on Windows)
* :attr:`st_atime` - time of most recent access expressed in seconds,
* :attr:`st_mtime` - time of most recent content modification
expressed in seconds,
* :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
available:
@ -2054,6 +2062,14 @@ Files and Directories
or FAT32 file systems, :attr:`st_mtime` has 2-second resolution, and
:attr:`st_atime` has only 1-day resolution. See your operating system
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
as a tuple of at least 10 integers giving the most important (and portable)
@ -2081,6 +2097,10 @@ Files and Directories
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])

View File

@ -44,6 +44,10 @@ PyAPI_FUNC(int) _PyTime_ObjectToTime_t(
PyObject *obj,
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.
usec is in the range [0; 999999] and rounded towards zero.
For example, -1.2 is converted to (-2, 800000). */

View File

@ -191,6 +191,13 @@ class StatAttributeTests(unittest.TestCase):
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 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:
result[200]
self.fail("No exception thrown")

View File

@ -2362,17 +2362,6 @@ run_in_subinterp(PyObject *self, PyObject *args)
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 *
test_pytime_object_to_time_t(PyObject *self, PyObject *args)
{

View File

@ -1550,6 +1550,9 @@ static PyStructSequence_Field stat_result_fields[] = {
{"st_atime", "time of last access"},
{"st_mtime", "time of last modification"},
{"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
{"st_blksize", "blocksize for filesystem I/O"},
#endif
@ -1572,9 +1575,9 @@ static PyStructSequence_Field stat_result_fields[] = {
};
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
#define ST_BLKSIZE_IDX 13
#define ST_BLKSIZE_IDX 16
#else
#define ST_BLKSIZE_IDX 12
#define ST_BLKSIZE_IDX 15
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
@ -1726,25 +1729,50 @@ stat_float_times(PyObject* self, PyObject *args)
return Py_None;
}
static PyObject *billion = NULL;
static void
fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
{
PyObject *fval,*ival;
#if SIZEOF_TIME_T > SIZEOF_LONG
ival = PyLong_FromLongLong((PY_LONG_LONG)sec);
#else
ival = PyLong_FromLong((long)sec);
#endif
if (!ival)
return;
PyObject *s = _PyLong_FromTime_t(sec);
PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
PyObject *s_in_ns = NULL;
PyObject *ns_total = NULL;
PyObject *float_s = NULL;
if (!(s && ns_fractional))
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) {
fval = PyFloat_FromDouble(sec + 1e-9*nsec);
} else {
fval = ival;
Py_INCREF(fval);
float_s = PyFloat_FromDouble(sec + 1e-9*nsec);
if (!float_s)
goto exit;
}
PyStructSequence_SET_ITEM(v, index, ival);
PyStructSequence_SET_ITEM(v, index+3, fval);
else {
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
@ -11627,6 +11655,10 @@ INITFUNC(void)
PyModule_AddObject(m, "terminal_size", (PyObject*) &TerminalSizeType);
billion = PyLong_FromLong(1000000000);
if (!billion)
return NULL;
return m;
}

View File

@ -96,6 +96,17 @@ _PyLong_AsTime_t(PyObject *obj)
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
_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
double denominator)