summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLarry Hastings <larry@hastings.org>2012-04-19 22:07:49 (GMT)
committerLarry Hastings <larry@hastings.org>2012-04-19 22:07:49 (GMT)
commit6fe20b3aee076850e390ef8bb2078b1cd09ddf88 (patch)
tree235f93dbd1d92f3983b6e4a4f1d84db296420365
parentdd5aa36f179011f390a0cc6fb4ba7bba1764f1a9 (diff)
downloadcpython-6fe20b3aee076850e390ef8bb2078b1cd09ddf88.zip
cpython-6fe20b3aee076850e390ef8bb2078b1cd09ddf88.tar.gz
cpython-6fe20b3aee076850e390ef8bb2078b1cd09ddf88.tar.bz2
Issue #14127: Add st_{cma}time_ns fields to os.stat() result object.
-rw-r--r--Doc/library/os.rst32
-rw-r--r--Include/pytime.h4
-rw-r--r--Lib/test/test_os.py7
-rw-r--r--Modules/_testcapimodule.c11
-rw-r--r--Modules/posixmodule.c64
-rw-r--r--Python/pytime.c11
6 files changed, 96 insertions, 33 deletions
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 74b89b8..14f9e07 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -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])
diff --git a/Include/pytime.h b/Include/pytime.h
index 0473dc7..221279b 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -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). */
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index b85d97d..aa619a8 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -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")
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index ec52a09..471d66d 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -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)
{
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 03b3b95..3713d1c 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -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;
+ }
+ else {
+ float_s = s;
+ Py_INCREF(float_s);
}
- PyStructSequence_SET_ITEM(v, index, ival);
- PyStructSequence_SET_ITEM(v, index+3, fval);
+
+ 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;
}
diff --git a/Python/pytime.c b/Python/pytime.c
index e7dadc7..db3f683 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -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)