[Python-checkins] cpython: Issue #14127: Add st_{cma}time_ns fields to os.stat() result object.

larry.hastings python-checkins at python.org
Fri Apr 20 00:08:46 CEST 2012


http://hg.python.org/cpython/rev/f554043badec
changeset:   76423:f554043badec
user:        Larry Hastings <larry at hastings.org>
date:        Thu Apr 19 15:07:49 2012 -0700
summary:
  Issue #14127: Add st_{cma}time_ns fields to os.stat() result object.

files:
  Doc/library/os.rst        |  32 ++++++++++--
  Include/pytime.h          |   4 +
  Lib/test/test_os.py       |   7 ++
  Modules/_testcapimodule.c |  11 ----
  Modules/posixmodule.c     |  66 ++++++++++++++++++++------
  Python/pytime.c           |  11 ++++
  6 files changed, 97 insertions(+), 34 deletions(-)


diff --git a/Doc/library/os.rst b/Doc/library/os.rst
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -2011,8 +2011,8 @@
    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 @@
    * :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 @@
       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 @@
 
    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
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -44,6 +44,10 @@
     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
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -191,6 +191,13 @@
                                   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
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2362,17 +2362,6 @@
     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
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1550,6 +1550,9 @@
     {"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 @@
 };
 
 #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 @@
     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);
-    }
-    PyStructSequence_SET_ITEM(v, index, ival);
-    PyStructSequence_SET_ITEM(v, index+3, 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, 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 @@
 
     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
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -96,6 +96,17 @@
     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)

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list