]> arthur.barton.de Git - bup.git/commitdiff
Move stat-related work to bup.xstat; add xstat.stat.
authorRob Browning <rlb@defaultvalue.org>
Sun, 24 Oct 2010 23:43:03 +0000 (18:43 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sun, 24 Oct 2010 23:56:34 +0000 (18:56 -0500)
Move helpers FSTime, lstat, fstat, lutime, lutime, and utime to a new
lib/bup/xstat.py file so that we can add a stat() definition that
won't conflict with the Python stat module when other code calls "from
bup.helpers import *".  Move xstat related tests to
lub/bup/t/txstat.py.

Add xstat.stat() based on _helpers.stat().

lib/bup/_helpers.c
lib/bup/helpers.py
lib/bup/metadata.py
lib/bup/t/thelpers.py
lib/bup/t/txstat.py [new file with mode: 0644]
lib/bup/xstat.py [new file with mode: 0644]

index 9eef6f224353ca1f56f8c0fdb519b6b38d90a355..30f11cf43be6e099d5ef57da94ab1c8b592ea19f 100644 (file)
@@ -336,6 +336,41 @@ static PyObject *bup_utimensat(PyObject *self, PyObject *args)
 
 #ifdef linux /* and likely others */
 
+#define HAVE_BUP_STAT 1
+static PyObject *bup_stat(PyObject *self, PyObject *args)
+{
+    int rc;
+    char *filename;
+
+    if (!PyArg_ParseTuple(args, "s", &filename))
+        return NULL;
+
+    struct stat st;
+    rc = stat(filename, &st);
+    if (rc != 0)
+        return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
+
+    return Py_BuildValue("kkkkkkkk"
+                         "(ll)"
+                         "(ll)"
+                         "(ll)",
+                         (unsigned long) st.st_mode,
+                         (unsigned long) st.st_ino,
+                         (unsigned long) st.st_dev,
+                         (unsigned long) st.st_nlink,
+                         (unsigned long) st.st_uid,
+                         (unsigned long) st.st_gid,
+                         (unsigned long) st.st_rdev,
+                         (unsigned long) st.st_size,
+                         (long) st.st_atime,
+                         (long) st.st_atim.tv_nsec,
+                         (long) st.st_mtime,
+                         (long) st.st_mtim.tv_nsec,
+                         (long) st.st_ctime,
+                         (long) st.st_ctim.tv_nsec);
+}
+
+
 #define HAVE_BUP_LSTAT 1
 static PyObject *bup_lstat(PyObject *self, PyObject *args)
 {
@@ -370,6 +405,7 @@ static PyObject *bup_lstat(PyObject *self, PyObject *args)
                          (long) st.st_ctim.tv_nsec);
 }
 
+
 #define HAVE_BUP_FSTAT 1
 static PyObject *bup_fstat(PyObject *self, PyObject *args)
 {
@@ -435,6 +471,10 @@ static PyMethodDef helper_methods[] = {
     { "utimensat", bup_utimensat, METH_VARARGS,
       "Change file timestamps with nanosecond precision." },
 #endif
+#ifdef HAVE_BUP_STAT
+    { "stat", bup_stat, METH_VARARGS,
+      "Extended version of stat." },
+#endif
 #ifdef HAVE_BUP_LSTAT
     { "lstat", bup_lstat, METH_VARARGS,
       "Extended version of lstat." },
@@ -457,7 +497,7 @@ PyMODINIT_FUNC init_helpers(void)
     PyModule_AddObject(m, "AT_SYMLINK_NOFOLLOW",
                        Py_BuildValue("i", AT_SYMLINK_NOFOLLOW));
 #endif
-#ifdef HAVE_BUP_LSTAT
+#ifdef HAVE_BUP_STAT
     PyModule_AddObject(m, "_have_ns_fs_timestamps", Py_BuildValue("i", 1));
 #endif
 }
index 53b94596f1b718be8e00541ff763395f4e70ac88..71b0f9a11dab6d3eb9dabe3bbbdb582f0e66a217 100644 (file)
@@ -413,152 +413,6 @@ def parse_date_or_fatal(str, fatal):
         return date
 
 
-class FSTime():
-    # Class to represent filesystem timestamps.  Use integer
-    # nanoseconds on platforms where we have the higher resolution
-    # lstat.  Use the native python stat representation (floating
-    # point seconds) otherwise.
-
-    def __cmp__(self, x):
-        return self._value.__cmp__(x._value)
-
-    def to_timespec(self):
-        """Return (s, ns) where ns is always non-negative
-        and t = s + ns / 10e8""" # metadata record rep (and libc rep)
-        s_ns = self.secs_nsecs()
-        if s_ns[0] > 0 or s_ns[1] >= 0:
-            return s_ns
-        return (s_ns[0] - 1, 10**9 + s_ns[1]) # ns is negative
-
-    if _helpers.lstat: # Use integer nanoseconds.
-
-        @staticmethod
-        def from_secs(secs):
-            ts = FSTime()
-            ts._value = int(secs * 10**9)
-            return ts
-
-        @staticmethod
-        def from_timespec(timespec):
-            ts = FSTime()
-            ts._value = timespec[0] * 10**9 + timespec[1]
-            return ts
-
-        @staticmethod
-        def from_stat_time(stat_time):
-            return FSTime.from_timespec(stat_time)
-
-        def approx_secs(self):
-            return self._value / 10e8;
-
-        def secs_nsecs(self):
-            "Return a (s, ns) pair: -1.5s -> (-1, -10**9 / 2)."
-            if self._value >= 0:
-                return (self._value / 10**9, self._value % 10**9)
-            abs_val = -self._value
-            return (- (abs_val / 10**9), - (abs_val % 10**9))
-
-    else: # Use python default floating-point seconds.
-
-        @staticmethod
-        def from_secs(secs):
-            ts = FSTime()
-            ts._value = secs
-            return ts
-
-        @staticmethod
-        def from_timespec(timespec):
-            ts = FSTime()
-            ts._value = timespec[0] + (timespec[1] / 10e8)
-            return ts
-
-        @staticmethod
-        def from_stat_time(stat_time):
-            ts = FSTime()
-            ts._value = stat_time
-            return ts
-
-        def approx_secs(self):
-            return self._value
-
-        def secs_nsecs(self):
-            "Return a (s, ns) pair: -1.5s -> (-1, -5**9)."
-            x = math.modf(self._value)
-            return (x[1], x[0] * 10**9)
-
-
-def lutime(path, times):
-    if _helpers.utimensat:
-        atime = times[0].to_timespec()
-        mtime = times[1].to_timespec()
-        return _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime),
-                                  _helpers.AT_SYMLINK_NOFOLLOW)
-    else:
-        return None
-
-
-def utime(path, times):
-    if _helpers.utimensat:
-        atime = times[0].to_timespec()
-        mtime = times[1].to_timespec()
-        return _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime), 0)
-    else:
-        atime = times[0].approx_secs()
-        mtime = times[1].approx_secs()
-        os.utime(path, (atime, mtime))
-
-
-class stat_result():
-
-    @staticmethod
-    def from_stat_rep(st):
-        result = stat_result()
-        if _helpers._have_ns_fs_timestamps:
-            (result.st_mode,
-             result.st_ino,
-             result.st_dev,
-             result.st_nlink,
-             result.st_uid,
-             result.st_gid,
-             result.st_rdev,
-             result.st_size,
-             atime,
-             mtime,
-             ctime) = st
-        else:
-            result.st_mode = st.st_mode
-            result.st_ino = st.st_ino
-            result.st_dev = st.st_dev
-            result.st_nlink = st.st_nlink
-            result.st_uid = st.st_uid
-            result.st_gid = st.st_gid
-            result.st_rdev = st.st_rdev
-            result.st_size = st.st_size
-            atime = FSTime.from_stat_time(st.st_atime)
-            mtime = FSTime.from_stat_time(st.st_mtime)
-            ctime = FSTime.from_stat_time(st.st_ctime)
-        result.st_atime = FSTime.from_stat_time(atime)
-        result.st_mtime = FSTime.from_stat_time(mtime)
-        result.st_ctime = FSTime.from_stat_time(ctime)
-        return result
-
-
-def fstat(path):
-    if _helpers.fstat:
-        st = _helpers.fstat(path)
-    else:
-        st = os.fstat(path)
-    return stat_result.from_stat_rep(st)
-
-
-def lstat(path):
-    if _helpers.lstat:
-        st = _helpers.lstat(path)
-    else:
-        st = os.lstat(path)
-    return stat_result.from_stat_rep(st)
-
-
 # hashlib is only available in python 2.5 or higher, but the 'sha' module
 # produces a DeprecationWarning in python 2.6 or higher.  We want to support
 # python 2.4 and above without any stupid warnings, so let's try using hashlib
index d520f5129a7fb157aace901ce77f1d99b6f6f15a..117df31318983044d484c5618d486951e8b735e2 100644 (file)
@@ -9,7 +9,8 @@ import errno, os, sys, stat, pwd, grp, struct, xattr, posix1e, re
 
 from cStringIO import StringIO
 from bup import vint
-from bup.helpers import add_error, mkdirp, log, utime, lutime, lstat, FSTime
+from bup.helpers import add_error, mkdirp, log
+from bup.xstat import utime, lutime, lstat, FSTime
 import bup._helpers as _helpers
 
 if _helpers.get_linux_file_attr:
index 6ca4b9d65ca70a195daac8183e05d2bd705cb4d0..18f5e890bf413edfd8d97e9ef9f03fed46cab6a0 100644 (file)
@@ -21,56 +21,3 @@ def test_detect_fakeroot():
         WVPASS(detect_fakeroot())
     else:
         WVPASS(not detect_fakeroot())
-
-
-def _test_fstime():
-    def approx_eq(x, y):
-        return math.fabs(x - y) < 1 / 10e8
-    def ts_eq_ish(x, y):
-        return approx_eq(x[0], y[0]) and approx_eq(x[1], y[1])
-    def fst_eq_ish(x, y):
-        return approx_eq(x.approx_secs(), y.approx_secs())
-    def s_ns_eq_ish(fst, s, ns):
-        (fst_s, fst_ns) = fst.secs_nsecs()
-        return approx_eq(fst_s, s) and approx_eq(fst_ns, ns)
-    from_secs = FSTime.from_secs
-    from_ts = FSTime.from_timespec
-    WVPASS(from_secs(0) == from_secs(0))
-    WVPASS(from_secs(0) < from_secs(1))
-    WVPASS(from_secs(-1) < from_secs(1))
-    WVPASS(from_secs(1) > from_secs(0))
-    WVPASS(from_secs(1) > from_secs(-1))
-
-    WVPASS(fst_eq_ish(from_secs(0), from_ts((0, 0))))
-    WVPASS(fst_eq_ish(from_secs(1), from_ts((1, 0))))
-    WVPASS(fst_eq_ish(from_secs(-1), from_ts((-1, 0))))
-    WVPASS(fst_eq_ish(from_secs(-0.5), from_ts((-1, 10**9 / 2))))
-    WVPASS(fst_eq_ish(from_secs(-1.5), from_ts((-2, 10**9 / 2))))
-    WVPASS(fst_eq_ish(from_secs(1 / 10e8), from_ts((0, 1))))
-    WVPASS(fst_eq_ish(from_secs(-1 / 10e8), from_ts((-1, 10**9 - 1))))
-
-    WVPASS(ts_eq_ish(from_secs(0).to_timespec(), (0, 0)))
-    WVPASS(ts_eq_ish(from_secs(1).to_timespec(), (1, 0)))
-    WVPASS(ts_eq_ish(from_secs(-1).to_timespec(), (-1, 0)))
-    WVPASS(ts_eq_ish(from_secs(-0.5).to_timespec(), (-1, 10**9 / 2)))
-    WVPASS(ts_eq_ish(from_secs(-1.5).to_timespec(), (-2, 10**9 / 2)))
-    WVPASS(ts_eq_ish(from_secs(1 / 10e8).to_timespec(), (0, 1)))
-    WVPASS(ts_eq_ish(from_secs(-1 / 10e8).to_timespec(), (-1, 10**9 - 1)))
-
-    WVPASS(s_ns_eq_ish(from_secs(0), 0, 0))
-    WVPASS(s_ns_eq_ish(from_secs(1), 1, 0))
-    WVPASS(s_ns_eq_ish(from_secs(-1), -1, 0))
-    WVPASS(s_ns_eq_ish(from_secs(-0.5), 0, - 10**9 / 2))
-    WVPASS(s_ns_eq_ish(from_secs(-1.5), -1, - 10**9 / 2))
-    WVPASS(s_ns_eq_ish(from_secs(-1 / 10e8), 0, -1))
-
-@wvtest
-def test_fstime():
-    _test_fstime();
-    if _helpers._have_ns_fs_timestamps: # Test native python timestamp rep too.
-        orig = _helpers._have_ns_fs_timestamps
-        try:
-            _helpers._have_ns_fs_timestamps = None
-            _test_fstime();
-        finally:
-            _helpers._have_ns_fs_timestamps = orig
diff --git a/lib/bup/t/txstat.py b/lib/bup/t/txstat.py
new file mode 100644 (file)
index 0000000..a56ac05
--- /dev/null
@@ -0,0 +1,56 @@
+import math
+from wvtest import *
+import bup._helpers as _helpers
+from bup.xstat import FSTime
+
+def _test_fstime():
+    def approx_eq(x, y):
+        return math.fabs(x - y) < 1 / 10e8
+    def ts_eq_ish(x, y):
+        return approx_eq(x[0], y[0]) and approx_eq(x[1], y[1])
+    def fst_eq_ish(x, y):
+        return approx_eq(x.approx_secs(), y.approx_secs())
+    def s_ns_eq_ish(fst, s, ns):
+        (fst_s, fst_ns) = fst.secs_nsecs()
+        return approx_eq(fst_s, s) and approx_eq(fst_ns, ns)
+    from_secs = FSTime.from_secs
+    from_ts = FSTime.from_timespec
+    WVPASS(from_secs(0) == from_secs(0))
+    WVPASS(from_secs(0) < from_secs(1))
+    WVPASS(from_secs(-1) < from_secs(1))
+    WVPASS(from_secs(1) > from_secs(0))
+    WVPASS(from_secs(1) > from_secs(-1))
+
+    WVPASS(fst_eq_ish(from_secs(0), from_ts((0, 0))))
+    WVPASS(fst_eq_ish(from_secs(1), from_ts((1, 0))))
+    WVPASS(fst_eq_ish(from_secs(-1), from_ts((-1, 0))))
+    WVPASS(fst_eq_ish(from_secs(-0.5), from_ts((-1, 10**9 / 2))))
+    WVPASS(fst_eq_ish(from_secs(-1.5), from_ts((-2, 10**9 / 2))))
+    WVPASS(fst_eq_ish(from_secs(1 / 10e8), from_ts((0, 1))))
+    WVPASS(fst_eq_ish(from_secs(-1 / 10e8), from_ts((-1, 10**9 - 1))))
+
+    WVPASS(ts_eq_ish(from_secs(0).to_timespec(), (0, 0)))
+    WVPASS(ts_eq_ish(from_secs(1).to_timespec(), (1, 0)))
+    WVPASS(ts_eq_ish(from_secs(-1).to_timespec(), (-1, 0)))
+    WVPASS(ts_eq_ish(from_secs(-0.5).to_timespec(), (-1, 10**9 / 2)))
+    WVPASS(ts_eq_ish(from_secs(-1.5).to_timespec(), (-2, 10**9 / 2)))
+    WVPASS(ts_eq_ish(from_secs(1 / 10e8).to_timespec(), (0, 1)))
+    WVPASS(ts_eq_ish(from_secs(-1 / 10e8).to_timespec(), (-1, 10**9 - 1)))
+
+    WVPASS(s_ns_eq_ish(from_secs(0), 0, 0))
+    WVPASS(s_ns_eq_ish(from_secs(1), 1, 0))
+    WVPASS(s_ns_eq_ish(from_secs(-1), -1, 0))
+    WVPASS(s_ns_eq_ish(from_secs(-0.5), 0, - 10**9 / 2))
+    WVPASS(s_ns_eq_ish(from_secs(-1.5), -1, - 10**9 / 2))
+    WVPASS(s_ns_eq_ish(from_secs(-1 / 10e8), 0, -1))
+
+@wvtest
+def test_fstime():
+    _test_fstime();
+    if _helpers._have_ns_fs_timestamps: # Test native python timestamp rep too.
+        orig = _helpers._have_ns_fs_timestamps
+        try:
+            _helpers._have_ns_fs_timestamps = None
+            _test_fstime();
+        finally:
+            _helpers._have_ns_fs_timestamps = orig
diff --git a/lib/bup/xstat.py b/lib/bup/xstat.py
new file mode 100644 (file)
index 0000000..696cc14
--- /dev/null
@@ -0,0 +1,156 @@
+"""Enhanced stat operations for bup."""
+import os
+import bup._helpers as _helpers
+
+class FSTime():
+    # Class to represent filesystem timestamps.  Use integer
+    # nanoseconds on platforms where we have the higher resolution
+    # lstat.  Use the native python stat representation (floating
+    # point seconds) otherwise.
+
+    def __cmp__(self, x):
+        return self._value.__cmp__(x._value)
+
+    def to_timespec(self):
+        """Return (s, ns) where ns is always non-negative
+        and t = s + ns / 10e8""" # metadata record rep (and libc rep)
+        s_ns = self.secs_nsecs()
+        if s_ns[0] > 0 or s_ns[1] >= 0:
+            return s_ns
+        return (s_ns[0] - 1, 10**9 + s_ns[1]) # ns is negative
+
+    if _helpers.lstat: # Use integer nanoseconds.
+
+        @staticmethod
+        def from_secs(secs):
+            ts = FSTime()
+            ts._value = int(secs * 10**9)
+            return ts
+
+        @staticmethod
+        def from_timespec(timespec):
+            ts = FSTime()
+            ts._value = timespec[0] * 10**9 + timespec[1]
+            return ts
+
+        @staticmethod
+        def from_stat_time(stat_time):
+            return FSTime.from_timespec(stat_time)
+
+        def approx_secs(self):
+            return self._value / 10e8;
+
+        def secs_nsecs(self):
+            "Return a (s, ns) pair: -1.5s -> (-1, -10**9 / 2)."
+            if self._value >= 0:
+                return (self._value / 10**9, self._value % 10**9)
+            abs_val = -self._value
+            return (- (abs_val / 10**9), - (abs_val % 10**9))
+
+    else: # Use python default floating-point seconds.
+
+        @staticmethod
+        def from_secs(secs):
+            ts = FSTime()
+            ts._value = secs
+            return ts
+
+        @staticmethod
+        def from_timespec(timespec):
+            ts = FSTime()
+            ts._value = timespec[0] + (timespec[1] / 10e8)
+            return ts
+
+        @staticmethod
+        def from_stat_time(stat_time):
+            ts = FSTime()
+            ts._value = stat_time
+            return ts
+
+        def approx_secs(self):
+            return self._value
+
+        def secs_nsecs(self):
+            "Return a (s, ns) pair: -1.5s -> (-1, -5**9)."
+            x = math.modf(self._value)
+            return (x[1], x[0] * 10**9)
+
+
+def lutime(path, times):
+    if _helpers.utimensat:
+        atime = times[0].to_timespec()
+        mtime = times[1].to_timespec()
+        return _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime),
+                                  _helpers.AT_SYMLINK_NOFOLLOW)
+    else:
+        return None
+
+
+def utime(path, times):
+    if _helpers.utimensat:
+        atime = times[0].to_timespec()
+        mtime = times[1].to_timespec()
+        return _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime), 0)
+    else:
+        atime = times[0].approx_secs()
+        mtime = times[1].approx_secs()
+        os.utime(path, (atime, mtime))
+
+
+class stat_result():
+
+    @staticmethod
+    def from_stat_rep(st):
+        result = stat_result()
+        if _helpers._have_ns_fs_timestamps:
+            (result.st_mode,
+             result.st_ino,
+             result.st_dev,
+             result.st_nlink,
+             result.st_uid,
+             result.st_gid,
+             result.st_rdev,
+             result.st_size,
+             atime,
+             mtime,
+             ctime) = st
+        else:
+            result.st_mode = st.st_mode
+            result.st_ino = st.st_ino
+            result.st_dev = st.st_dev
+            result.st_nlink = st.st_nlink
+            result.st_uid = st.st_uid
+            result.st_gid = st.st_gid
+            result.st_rdev = st.st_rdev
+            result.st_size = st.st_size
+            atime = FSTime.from_stat_time(st.st_atime)
+            mtime = FSTime.from_stat_time(st.st_mtime)
+            ctime = FSTime.from_stat_time(st.st_ctime)
+        result.st_atime = FSTime.from_stat_time(atime)
+        result.st_mtime = FSTime.from_stat_time(mtime)
+        result.st_ctime = FSTime.from_stat_time(ctime)
+        return result
+
+
+def stat(path):
+    if _helpers.stat:
+        st = _helpers.stat(path)
+    else:
+        st = os.stat(path)
+    return stat_result.from_stat_rep(st)
+
+
+def fstat(path):
+    if _helpers.fstat:
+        st = _helpers.fstat(path)
+    else:
+        st = os.fstat(path)
+    return stat_result.from_stat_rep(st)
+
+
+def lstat(path):
+    if _helpers.lstat:
+        st = _helpers.lstat(path)
+    else:
+        st = os.lstat(path)
+    return stat_result.from_stat_rep(st)