[Python-checkins] cpython: Issue #12904: os.utime, os.futimes, os.lutimes, and os.futimesat now write

larry.hastings python-checkins at python.org
Fri Sep 9 04:29:04 CEST 2011


http://hg.python.org/cpython/rev/1de6619733d9
changeset:   72315:1de6619733d9
user:        Larry Hastings <larry at hastings.org>
date:        Thu Sep 08 19:29:07 2011 -0700
summary:
  Issue #12904: os.utime, os.futimes, os.lutimes, and os.futimesat now write
atime and mtime with nanosecond precision on modern POSIX platforms.

files:
  Misc/NEWS             |    3 +
  Modules/posixmodule.c |  154 +++++++++++++++++++++--------
  2 files changed, 114 insertions(+), 43 deletions(-)


diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
 Core and Builtins
 -----------------
 
+- Issue #12904: os.utime, os.futimes, os.lutimes, and os.futimesat now write
+  atime and mtime with nanosecond precision on modern POSIX platforms.
+
 - Issue #12802: the Windows error ERROR_DIRECTORY (numbered 267) is now
   mapped to POSIX errno ENOTDIR (previously EINVAL).
 
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3417,6 +3417,30 @@
 }
 #endif /* HAVE_UNAME */
 
+
+/*
+ * Classic POSIX utime functions supported microseconds (1m/sec).
+ * Newer POSIX functions support nanoseconds (1 billion per sec).
+ * posixmodule now uses the new functions where possible.
+ * This improves accuracy in many situations, for example shutil.copy2().
+ *
+ * The implementation isn't currently sophisticated enough to handle
+ * a platform where HAVE_UTIMENSAT is true but HAVE_FUTIMENS is false.
+ * Specifically, posix_futimes() would break.
+ *
+ * Supporting such a platform wouldn't be impossible; you'd need two
+ * extract_time() functions, or make its precision a parameter.
+ * Since such a platform seems unlikely we haven't bothered.
+ */
+#if defined(HAVE_UTIMENSAT)
+#define EXTRACT_TIME_PRECISION (1e9)
+#if !defined(HAVE_FUTIMENS)
+#error You HAVE_UTIMENSAT but not HAVE_FUTIMENS... please see accompanying comment.
+#endif
+#else
+#define EXTRACT_TIME_PRECISION (1e6)
+#endif
+
 static int
 extract_time(PyObject *t, time_t* sec, long* usec)
 {
@@ -3435,7 +3459,8 @@
         if (intval == -1 && PyErr_Occurred())
             return -1;
         *sec = intval;
-        *usec = (long)((tval - intval) * 1e6); /* can't exceed 1000000 */
+
+        *usec = (long)((tval - intval) * EXTRACT_TIME_PRECISION);
         if (*usec < 0)
             /* If rounding gave us a negative number,
                truncate.  */
@@ -3553,24 +3578,6 @@
     int res;
     PyObject* arg;
 
-#if defined(HAVE_UTIMES)
-    struct timeval buf[2];
-#define ATIME buf[0].tv_sec
-#define MTIME buf[1].tv_sec
-#elif defined(HAVE_UTIME_H)
-/* XXX should define struct utimbuf instead, above */
-    struct utimbuf buf;
-#define ATIME buf.actime
-#define MTIME buf.modtime
-#define UTIME_ARG &buf
-#else /* HAVE_UTIMES */
-    time_t buf[2];
-#define ATIME buf[0]
-#define MTIME buf[1]
-#define UTIME_ARG buf
-#endif /* HAVE_UTIMES */
-
-
     if (!PyArg_ParseTuple(args, "O&O:utime",
                           PyUnicode_FSConverter, &opath, &arg))
         return NULL;
@@ -3598,19 +3605,37 @@
             Py_DECREF(opath);
             return NULL;
         }
-        ATIME = atime;
-        MTIME = mtime;
-#ifdef HAVE_UTIMES
+
+        Py_BEGIN_ALLOW_THREADS
+        {
+#ifdef HAVE_UTIMENSAT
+        struct timespec buf[2];
+        buf[0].tv_sec = atime;
+        buf[0].tv_nsec = ausec;
+        buf[1].tv_sec = mtime;
+        buf[1].tv_nsec = musec;
+        res = utimensat(AT_FDCWD, path, buf, 0);
+#elif defined(HAVE_UTIMES)
+        struct timeval buf[2];
+        buf[0].tv_sec = atime;
         buf[0].tv_usec = ausec;
+        buf[1].tv_sec = mtime;
         buf[1].tv_usec = musec;
-        Py_BEGIN_ALLOW_THREADS
         res = utimes(path, buf);
+#elif defined(HAVE_UTIME_H)
+        /* XXX should define struct utimbuf instead, above */
+        struct utimbuf buf;
+        buf.actime = atime;
+        buf.modtime = mtime;
+        res = utime(path, &buf);
+#else
+        time_t buf[2];
+        buf[0] = atime;
+        buf[1] = mtime;
+        res = utime(path, buf);
+#endif
+        }
         Py_END_ALLOW_THREADS
-#else
-        Py_BEGIN_ALLOW_THREADS
-        res = utime(path, UTIME_ARG);
-        Py_END_ALLOW_THREADS
-#endif /* HAVE_UTIMES */
     }
     if (res < 0) {
         return posix_error_with_allocated_filename(opath);
@@ -3618,9 +3643,7 @@
     Py_DECREF(opath);
     Py_INCREF(Py_None);
     return Py_None;
-#undef UTIME_ARG
-#undef ATIME
-#undef MTIME
+#undef UTIME_EXTRACT
 #endif /* MS_WINDOWS */
 }
 
@@ -3637,7 +3660,7 @@
 {
     int res, fd;
     PyObject* arg;
-    struct timeval buf[2];
+    time_t atime, mtime;
     long ausec, musec;
 
     if (!PyArg_ParseTuple(args, "iO:futimes", &fd, &arg))
@@ -3656,17 +3679,31 @@
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                &(buf[0].tv_sec), &ausec) == -1) {
+                &atime, &ausec) == -1) {
             return NULL;
         }
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                &(buf[1].tv_sec), &musec) == -1) {
+                &mtime, &musec) == -1) {
             return NULL;
         }
+        Py_BEGIN_ALLOW_THREADS
+        {
+#ifdef HAVE_FUTIMENS
+        struct timespec buf[2];
+        buf[0].tv_sec = atime;
+        buf[0].tv_nsec = ausec;
+        buf[1].tv_sec = mtime;
+        buf[1].tv_nsec = musec;
+        res = futimens(fd, buf);
+#else
+        struct timeval buf[2];
+        buf[0].tv_sec = atime;
         buf[0].tv_usec = ausec;
+        buf[1].tv_sec = mtime;
         buf[1].tv_usec = musec;
-        Py_BEGIN_ALLOW_THREADS
         res = futimes(fd, buf);
+#endif
+        }
         Py_END_ALLOW_THREADS
     }
     if (res < 0)
@@ -3687,7 +3724,7 @@
     PyObject *opath, *arg;
     const char *path;
     int res;
-    struct timeval buf[2];
+    time_t atime, mtime;
     long ausec, musec;
 
     if (!PyArg_ParseTuple(args, "O&O:lutimes",
@@ -3708,19 +3745,33 @@
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                &(buf[0].tv_sec), &ausec) == -1) {
+                &atime, &ausec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                &(buf[1].tv_sec), &musec) == -1) {
+                &mtime, &musec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
+        Py_BEGIN_ALLOW_THREADS
+        {
+#ifdef HAVE_UTIMENSAT
+        struct timespec buf[2];
+        buf[0].tv_sec = atime;
+        buf[0].tv_nsec = ausec;
+        buf[1].tv_sec = mtime;
+        buf[1].tv_nsec = musec;
+        res = utimensat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
+#else
+        struct timeval buf[2];
+        buf[0].tv_sec = atime;
         buf[0].tv_usec = ausec;
+        buf[1].tv_sec = mtime;
         buf[1].tv_usec = musec;
-        Py_BEGIN_ALLOW_THREADS
         res = lutimes(path, buf);
+#endif
+        }
         Py_END_ALLOW_THREADS
     }
     Py_DECREF(opath);
@@ -9558,8 +9609,8 @@
     char *path;
     int res, dirfd;
     PyObject* arg;
-
-    struct timeval buf[2];
+    time_t atime, mtime;
+    long ausec, musec;
 
     if (!PyArg_ParseTuple(args, "iO&O:futimesat",
             &dirfd, PyUnicode_FSConverter, &opath, &arg))
@@ -9579,17 +9630,34 @@
     }
     else {
         if (extract_time(PyTuple_GET_ITEM(arg, 0),
-                         &(buf[0].tv_sec), &(buf[0].tv_usec)) == -1) {
+                         &atime, &ausec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
         if (extract_time(PyTuple_GET_ITEM(arg, 1),
-                         &(buf[1].tv_sec), &(buf[1].tv_usec)) == -1) {
+                         &mtime, &musec) == -1) {
             Py_DECREF(opath);
             return NULL;
         }
+
         Py_BEGIN_ALLOW_THREADS
+        {
+#ifdef HAVE_UTIMENSAT
+        struct timespec buf[2];
+        buf[0].tv_sec = atime;
+        buf[0].tv_nsec = ausec;
+        buf[1].tv_sec = mtime;
+        buf[1].tv_nsec = musec;
+        res = utimensat(dirfd, path, buf, 0);
+#else
+        struct timeval buf[2];
+        buf[0].tv_sec = atime;
+        buf[0].tv_usec = ausec;
+        buf[1].tv_sec = mtime;
+        buf[1].tv_usec = musec;
         res = futimesat(dirfd, path, buf);
+#endif
+        }
         Py_END_ALLOW_THREADS
     }
     Py_DECREF(opath);

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


More information about the Python-checkins mailing list