Author: Richard Plangger <planri...@gmail.com>
Branch: ppc-vsx-support
Changeset: r87272:b8579705f7e6
Date: 2016-09-21 16:46 +0200
http://bitbucket.org/pypy/pypy/changeset/b8579705f7e6/

Log:    merge default

diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO
--- a/lib_pypy/cffi.egg-info/PKG-INFO
+++ b/lib_pypy/cffi.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: cffi
-Version: 1.8.3
+Version: 1.8.4
 Summary: Foreign Function Interface for Python calling C code.
 Home-page: http://cffi.readthedocs.org
 Author: Armin Rigo, Maciej Fijalkowski
diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -4,8 +4,8 @@
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
 
-__version__ = "1.8.3"
-__version_info__ = (1, 8, 3)
+__version__ = "1.8.4"
+__version_info__ = (1, 8, 4)
 
 # The verifier module file names are based on the CRC32 of a string that
 # contains the following version number.  It may be older than __version__
diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h
--- a/lib_pypy/cffi/_embedding.h
+++ b/lib_pypy/cffi/_embedding.h
@@ -233,7 +233,7 @@
         f = PySys_GetObject((char *)"stderr");
         if (f != NULL && f != Py_None) {
             PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
-                               "\ncompiled with cffi version: 1.8.3"
+                               "\ncompiled with cffi version: 1.8.4"
                                "\n_cffi_backend module: ", f);
             modules = PyImport_GetModuleDict();
             mod = PyDict_GetItemString(modules, "_cffi_backend");
diff --git a/lib_pypy/cffi/cparser.py b/lib_pypy/cffi/cparser.py
--- a/lib_pypy/cffi/cparser.py
+++ b/lib_pypy/cffi/cparser.py
@@ -332,7 +332,7 @@
                         realtype = model.unknown_ptr_type(decl.name)
                     else:
                         realtype, quals = self._get_type_and_quals(
-                            decl.type, name=decl.name)
+                            decl.type, name=decl.name, partial_length_ok=True)
                     self._declare('typedef ' + decl.name, realtype, 
quals=quals)
                 else:
                     raise api.CDefError("unrecognized construct", decl)
@@ -781,11 +781,14 @@
                 exprnode.name in self._int_constants):
             return self._int_constants[exprnode.name]
         #
-        if partial_length_ok:
-            if (isinstance(exprnode, pycparser.c_ast.ID) and
+        if (isinstance(exprnode, pycparser.c_ast.ID) and
                     exprnode.name == '__dotdotdotarray__'):
+            if partial_length_ok:
                 self._partial_length = True
                 return '...'
+            raise api.FFIError(":%d: unsupported '[...]' here, cannot derive "
+                               "the actual array length in this context"
+                               % exprnode.coord.line)
         #
         raise api.FFIError(":%d: unsupported expression: expected a "
                            "simple numeric constant" % exprnode.coord.line)
diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py
--- a/lib_pypy/cffi/recompiler.py
+++ b/lib_pypy/cffi/recompiler.py
@@ -587,8 +587,11 @@
     # ----------
     # typedefs
 
+    def _typedef_type(self, tp, name):
+        return self._global_type(tp, "(*(%s *)0)" % (name,))
+
     def _generate_cpy_typedef_collecttype(self, tp, name):
-        self._do_collect_type(tp)
+        self._do_collect_type(self._typedef_type(tp, name))
 
     def _generate_cpy_typedef_decl(self, tp, name):
         pass
@@ -598,6 +601,7 @@
         self._lsts["typename"].append(TypenameExpr(name, type_index))
 
     def _generate_cpy_typedef_ctx(self, tp, name):
+        tp = self._typedef_type(tp, name)
         self._typedef_ctx(tp, name)
         if getattr(tp, "origin", None) == "unknown_type":
             self._struct_ctx(tp, tp.name, approxname=None)
diff --git a/pypy/module/_cffi_backend/__init__.py 
b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -3,7 +3,7 @@
 from rpython.rlib import rdynload, clibffi, entrypoint
 from rpython.rtyper.lltypesystem import rffi
 
-VERSION = "1.8.3"
+VERSION = "1.8.4"
 
 FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI
 try:
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py 
b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1,7 +1,7 @@
 # ____________________________________________________________
 
 import sys
-assert __version__ == "1.8.3", ("This test_c.py file is for testing a version"
+assert __version__ == "1.8.4", ("This test_c.py file is for testing a version"
                                 " of cffi that differs from the one that we"
                                 " get from 'import _cffi_backend'")
 if sys.version_info < (3,):
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py 
b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -510,3 +510,8 @@
         q = ffi.new("char[]", "abcd")
         p = ffi.cast("char(*)(void)", q)
         raises(TypeError, ffi.string, p)
+
+    def test_negative_array_size(self):
+        import _cffi_backend as _cffi1_backend
+        ffi = _cffi1_backend.FFI()
+        raises(ffi.error, ffi.cast, "int[-5]", 0)
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py 
b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -8,7 +8,7 @@
 
 @unwrap_spec(cdef=str, module_name=str, source=str)
 def prepare(space, cdef, module_name, source, w_includes=None,
-            w_extra_source=None):
+            w_extra_source=None, w_min_version=None):
     try:
         import cffi
         from cffi import FFI            # <== the system one, which
@@ -16,8 +16,13 @@
         from cffi import ffiplatform
     except ImportError:
         py.test.skip("system cffi module not found or older than 1.0.0")
-    if cffi.__version_info__ < (1, 4, 0):
-        py.test.skip("system cffi module needs to be at least 1.4.0")
+    if w_min_version is None:
+        min_version = (1, 4, 0)
+    else:
+        min_version = tuple(space.unwrap(w_min_version))
+    if cffi.__version_info__ < min_version:
+        py.test.skip("system cffi module needs to be at least %s, got %s" % (
+            min_version, cffi.__version_info__))
     space.appexec([], """():
         import _cffi_backend     # force it to be initialized
     """)
@@ -1790,3 +1795,28 @@
                                 "void f(void) { }")
         assert lib.f.__get__(42) is lib.f
         assert lib.f.__get__(42, int) is lib.f
+
+    def test_typedef_array_dotdotdot(self):
+        ffi, lib = self.prepare("""
+            typedef int foo_t[...], bar_t[...];
+            int gv[...];
+            typedef int mat_t[...][...];
+            typedef int vmat_t[][...];
+            """,
+            "test_typedef_array_dotdotdot", """
+            typedef int foo_t[50], bar_t[50];
+            int gv[23];
+            typedef int mat_t[6][7];
+            typedef int vmat_t[][8];
+        """, min_version=(1, 8, 4))
+        assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int")
+        assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int")
+        assert len(ffi.new("foo_t")) == 50
+        assert len(ffi.new("bar_t")) == 50
+        assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int")
+        assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int")
+        assert len(ffi.new("mat_t")) == 6
+        assert len(ffi.new("mat_t")[3]) == 7
+        raises(ffi.error, ffi.sizeof, "vmat_t")
+        p = ffi.new("vmat_t", 4)
+        assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int")
diff --git a/pypy/module/array/interp_array.py 
b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -30,28 +30,25 @@
             raise oefmt(space.w_TypeError,
                         "array.array() does not take keyword arguments")
 
-    w_initializer_type = None
-    w_initializer = None
-    if len(__args__.arguments_w) > 0:
-        w_initializer = __args__.arguments_w[0]
-        w_initializer_type = space.type(w_initializer)
     for tc in unroll_typecodes:
         if typecode == tc:
             a = space.allocate_instance(types[tc].w_class, w_cls)
             a.__init__(space)
-            if w_initializer is not None:
-                if w_initializer_type is space.w_str:
-                    a.descr_fromstring(space, w_initializer)
-                elif w_initializer_type is space.w_list:
-                    a.descr_fromlist(space, w_initializer)
-                else:
-                    a.extend(w_initializer, True)
             break
     else:
         raise oefmt(space.w_ValueError,
                     "bad typecode (must be c, b, B, u, h, H, i, I, l, L, f or "
                     "d)")
 
+    if len(__args__.arguments_w) > 0:
+        w_initializer = __args__.arguments_w[0]
+        w_initializer_type = space.type(w_initializer)
+        if w_initializer_type is space.w_str:
+            a.descr_fromstring(space, w_initializer)
+        elif w_initializer_type is space.w_list:
+            a.descr_fromlist(space, w_initializer)
+        else:
+            a.extend(w_initializer, True)
     return a
 
 
diff --git a/pypy/module/posix/interp_posix.py 
b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -226,13 +226,13 @@
     w_keywords = space.newdict()
     stat_float_times = space.fromcache(StatState).stat_float_times
     for i, (name, TYPE) in FIELDS:
-        value = getattr(st, name)
-        if name in ('st_atime', 'st_mtime', 'st_ctime'):
-            value = int(value)   # rounded to an integer for indexed access
-        w_value = space.wrap(value)
         if i < rposix_stat.N_INDEXABLE_FIELDS:
+            # get the first 10 items by indexing; this gives us
+            # 'st_Xtime' as an integer, too
+            w_value = space.wrap(st[i])
             lst[i] = w_value
-        else:
+        elif name.startswith('st_'):    # exclude 'nsec_Xtime'
+            w_value = space.wrap(getattr(st, name))
             space.setitem(w_keywords, space.wrap(name), w_value)
 
     # non-rounded values for name-based access
@@ -243,13 +243,8 @@
                       space.wrap('st_mtime'), space.wrap(st.st_mtime))
         space.setitem(w_keywords,
                       space.wrap('st_ctime'), space.wrap(st.st_ctime))
-    else:
-        space.setitem(w_keywords,
-                      space.wrap('st_atime'), space.wrap(int(st.st_atime)))
-        space.setitem(w_keywords,
-                      space.wrap('st_mtime'), space.wrap(int(st.st_mtime)))
-        space.setitem(w_keywords,
-                      space.wrap('st_ctime'), space.wrap(int(st.st_ctime)))
+    #else:
+    #   filled by the __init__ method
 
     w_tuple = space.newtuple(lst)
     w_stat_result = space.getattr(space.getbuiltinmodule(os.name),
diff --git a/pypy/module/posix/test/test_posix2.py 
b/pypy/module/posix/test/test_posix2.py
--- a/pypy/module/posix/test/test_posix2.py
+++ b/pypy/module/posix/test/test_posix2.py
@@ -129,9 +129,9 @@
         assert st[4] == st.st_uid
         assert st[5] == st.st_gid
         assert st[6] == st.st_size
-        assert st[7] == int(st.st_atime)
-        assert st[8] == int(st.st_mtime)
-        assert st[9] == int(st.st_ctime)
+        assert st[7] == int(st.st_atime)   # in complete corner cases, rounding
+        assert st[8] == int(st.st_mtime)   # here could maybe get the wrong
+        assert st[9] == int(st.st_ctime)   # integer...
 
         assert stat.S_IMODE(st.st_mode) & stat.S_IRUSR
         assert stat.S_IMODE(st.st_mode) & stat.S_IWUSR
@@ -141,13 +141,12 @@
         assert st.st_size == 14
         assert st.st_nlink == 1
 
-        #if sys.platform.startswith('linux'):
-        #    # expects non-integer timestamps - it's unlikely that they are
-        #    # all three integers
-        #    assert ((st.st_atime, st.st_mtime, st.st_ctime) !=
-        #            (st[7],       st[8],       st[9]))
-        #    assert st.st_blksize * st.st_blocks >= st.st_size
+        assert not hasattr(st, 'nsec_atime')
+
         if sys.platform.startswith('linux'):
+            assert isinstance(st.st_atime, float)
+            assert isinstance(st.st_mtime, float)
+            assert isinstance(st.st_ctime, float)
             assert hasattr(st, 'st_rdev')
 
     def test_stat_float_times(self):
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
@@ -480,3 +480,7 @@
         assert ffi.unpack(p+1, 7) == b"bc\x00def\x00"
         p = ffi.new("int[]", [-123456789])
         assert ffi.unpack(p, 1) == [-123456789]
+
+    def test_negative_array_size(self):
+        ffi = FFI()
+        py.test.raises(ValueError, ffi.cast, "int[-5]", 0)
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
@@ -503,3 +503,7 @@
     assert ffi.unpack(p+1, 7) == b"bc\x00def\x00"
     p = ffi.new("int[]", [-123456789])
     assert ffi.unpack(p, 1) == [-123456789]
+
+def test_negative_array_size():
+    ffi = _cffi1_backend.FFI()
+    py.test.raises(ffi.error, ffi.cast, "int[-5]", 0)
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
@@ -1981,3 +1981,29 @@
         static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; }
     """)
     assert lib.f1(52).a == 52
+
+def test_typedef_array_dotdotdot():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef int foo_t[...], bar_t[...];
+        int gv[...];
+        typedef int mat_t[...][...];
+        typedef int vmat_t[][...];
+        """)
+    lib = verify(ffi, "test_typedef_array_dotdotdot", """
+        typedef int foo_t[50], bar_t[50];
+        int gv[23];
+        typedef int mat_t[6][7];
+        typedef int vmat_t[][8];
+    """)
+    assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int")
+    assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int")
+    assert len(ffi.new("foo_t")) == 50
+    assert len(ffi.new("bar_t")) == 50
+    assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int")
+    assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int")
+    assert len(ffi.new("mat_t")) == 6
+    assert len(ffi.new("mat_t")[3]) == 7
+    py.test.raises(ffi.error, ffi.sizeof, "vmat_t")
+    p = ffi.new("vmat_t", 4)
+    assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int")
diff --git a/rpython/rlib/rposix_stat.py b/rpython/rlib/rposix_stat.py
--- a/rpython/rlib/rposix_stat.py
+++ b/rpython/rlib/rposix_stat.py
@@ -17,7 +17,7 @@
 from rpython.rtyper.error import TyperError
 
 from rpython.rlib._os_support import _preferred_traits, string_traits
-from rpython.rlib.objectmodel import specialize
+from rpython.rlib.objectmodel import specialize, we_are_translated
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 from rpython.rlib.rarithmetic import intmask
@@ -51,15 +51,18 @@
     ("st_uid",       lltype.Signed),
     ("st_gid",       lltype.Signed),
     ("st_size",      lltype.SignedLongLong),
-    ("st_atime",     lltype.Float),
-    ("st_mtime",     lltype.Float),
-    ("st_ctime",     lltype.Float),
+    ("st_atime",     lltype.SignedLongLong),   # integral number of seconds
+    ("st_mtime",     lltype.SignedLongLong),   #
+    ("st_ctime",     lltype.SignedLongLong),   #
     ("st_blksize",   lltype.Signed),
     ("st_blocks",    lltype.Signed),
     ("st_rdev",      lltype.Signed),
     ("st_flags",     lltype.Signed),
     #("st_gen",       lltype.Signed),     -- new in CPy 2.5, not implemented
     #("st_birthtime", lltype.Float),      -- new in CPy 2.5, not implemented
+    ("nsec_atime",   lltype.Signed),   # number of nanoseconds
+    ("nsec_mtime",   lltype.Signed),   #
+    ("nsec_ctime",   lltype.Signed),   #
 ]
 N_INDEXABLE_FIELDS = 10
 
@@ -79,6 +82,37 @@
     ("f_namemax", lltype.Signed),
 ]
 
+@specialize.arg(1)
+def get_stat_ns_as_bigint(st, name):
+    """'name' is one of the strings "atime", "mtime" or "ctime".
+    Returns a bigint that represents the number of nanoseconds
+    stored inside the RPython-level os.stat_result 'st'.
+
+    Note that when running untranslated, the os.stat_result type
+    is from Python 2.7, which doesn't store more precision than
+    a float anyway.  You will only get more after translation.
+    """
+    from rpython.rlib.rbigint import rbigint
+
+    if not we_are_translated():
+        as_float = getattr(st, "st_" + name)
+        return rbigint.fromfloat(as_float * 1e9)
+
+    if name == "atime":
+        i, j = 7, -3
+    elif name == "mtime":
+        i, j = 8, -2
+    elif name == "ctime":
+        i, j = 9, -1
+    else:
+        raise AssertionError(name)
+
+    sec = st[i]
+    nsec = st[j]
+    result = rbigint.fromrarith_int(sec).int_mul(1000000000)
+    result = result.int_add(nsec)
+    return result
+
 
 # ____________________________________________________________
 #
@@ -97,7 +131,15 @@
         if not s_attr.is_constant():
             raise annmodel.AnnotatorError("non-constant attr name in 
getattr()")
         attrname = s_attr.const
-        TYPE = STAT_FIELD_TYPES[attrname]
+        if attrname in ('st_atime', 'st_mtime', 'st_ctime'):
+            # like CPython, in RPython we can read the st_Xtime
+            # attribute and get a floating-point result.  We can also
+            # get a full-precision bigint with get_stat_ns_as_bigint().
+            # The floating-point result is computed like a property
+            # by _ll_get_st_Xtime().
+            TYPE = lltype.Float
+        else:
+            TYPE = STAT_FIELD_TYPES[attrname]
         return lltype_to_annotation(TYPE)
 
     def _get_rmarshall_support_(self):     # for rlib.rmarshal
@@ -105,13 +147,14 @@
         # (we ignore the extra values here for simplicity and portability)
         def stat_result_reduce(st):
             return (st[0], st[1], st[2], st[3], st[4],
-                    st[5], st[6], st[7], st[8], st[9])
+                    st[5], st[6], st[7], st[8], st[9],
+                    st[-3], st[-2], st[-1])
 
         def stat_result_recreate(tup):
-            return make_stat_result(tup + extra_zeroes)
+            return make_stat_result(tup[:10] + extra_zeroes + tup[-3:])
         s_reduced = annmodel.SomeTuple([lltype_to_annotation(TYPE)
                                        for name, TYPE in PORTABLE_STAT_FIELDS])
-        extra_zeroes = (0,) * (len(STAT_FIELDS) - len(PORTABLE_STAT_FIELDS))
+        extra_zeroes = (0,) * (len(STAT_FIELDS) - len(PORTABLE_STAT_FIELDS) - 
3)
         return s_reduced, stat_result_reduce, stat_result_recreate
 
 
@@ -119,7 +162,7 @@
     def getitem((s_sta, s_int)):
         assert s_int.is_constant(), "os.stat()[index]: index must be constant"
         index = s_int.const
-        assert 0 <= index < N_INDEXABLE_FIELDS, "os.stat()[index] out of range"
+        assert -3 <= index < N_INDEXABLE_FIELDS, "os.stat()[index] out of 
range"
         name, TYPE = STAT_FIELDS[index]
         return lltype_to_annotation(TYPE)
 
@@ -152,28 +195,61 @@
     def rtype_getattr(self, hop):
         s_attr = hop.args_s[1]
         attr = s_attr.const
+        if attr in ('st_atime', 'st_mtime', 'st_ctime'):
+            ll_func = globals()['_ll_get_' + attr]
+            v_tuple = hop.inputarg(self, arg=0)
+            return hop.gendirectcall(ll_func, v_tuple)
         try:
             index = self.stat_field_indexes[attr]
         except KeyError:
             raise TyperError("os.stat().%s: field not available" % (attr,))
         return self.redispatch_getfield(hop, index)
 
+@specialize.memo()
+def _stfld(name):
+    index = STAT_FIELD_NAMES.index(name)
+    return 'item%d' % index
+
+def _ll_get_st_atime(tup):
+    return (float(getattr(tup, _stfld("st_atime"))) +
+            1E-9 * getattr(tup, _stfld("nsec_atime")))
+
+def _ll_get_st_mtime(tup):
+    return (float(getattr(tup, _stfld("st_mtime"))) +
+            1E-9 * getattr(tup, _stfld("nsec_mtime")))
+
+def _ll_get_st_ctime(tup):
+    return (float(getattr(tup, _stfld("st_ctime"))) +
+            1E-9 * getattr(tup, _stfld("nsec_ctime")))
+
 
 class __extend__(pairtype(StatResultRepr, IntegerRepr)):
     def rtype_getitem((r_sta, r_int), hop):
         s_int = hop.args_s[1]
         index = s_int.const
+        if index < 0:
+            index += len(STAT_FIELDS)
         return r_sta.redispatch_getfield(hop, index)
 
 s_StatResult = SomeStatResult()
 
+
 def make_stat_result(tup):
-    """Turn a tuple into an os.stat_result object."""
-    positional = tuple(
-        lltype.cast_primitive(TYPE, value) for value, (name, TYPE) in
-            zip(tup, STAT_FIELDS)[:N_INDEXABLE_FIELDS])
+    """NOT_RPYTHON: Turn a tuple into an os.stat_result object."""
+    assert len(tup) == len(STAT_FIELDS)
+    assert float not in [type(x) for x in tup]
+    positional = []
+    for i in range(N_INDEXABLE_FIELDS):
+        name, TYPE = STAT_FIELDS[i]
+        value = lltype.cast_primitive(TYPE, tup[i])
+        positional.append(value)
     kwds = {}
+    kwds['st_atime'] = tup[7] + 1e-9 * tup[-3]
+    kwds['st_mtime'] = tup[8] + 1e-9 * tup[-2]
+    kwds['st_ctime'] = tup[9] + 1e-9 * tup[-1]
     for value, (name, TYPE) in zip(tup, STAT_FIELDS)[N_INDEXABLE_FIELDS:]:
+        if name.startswith('nsec_'):
+            continue   # ignore the nsec_Xtime here
         kwds[name] = lltype.cast_primitive(TYPE, value)
     return os.stat_result(positional, kwds)
 
@@ -360,6 +436,8 @@
         posix_declaration(ALL_STAT_FIELDS[_i])
     del _i
 
+STAT_FIELDS += ALL_STAT_FIELDS[-3:]   # nsec_Xtime
+
 # these two global vars only list the fields defined in the underlying platform
 STAT_FIELD_TYPES = dict(STAT_FIELDS)      # {'st_xxx': TYPE}
 STAT_FIELD_NAMES = [_name for (_name, _TYPE) in STAT_FIELDS]
@@ -368,16 +446,20 @@
 STATVFS_FIELD_TYPES = dict(STATVFS_FIELDS)
 STATVFS_FIELD_NAMES = [name for name, tp in STATVFS_FIELDS]
 
+
 def build_stat_result(st):
     # only for LL backends
     if TIMESPEC is not None:
-        atim = st.c_st_atim; atime = int(atim.c_tv_sec) + 1E-9 * 
int(atim.c_tv_nsec)
-        mtim = st.c_st_mtim; mtime = int(mtim.c_tv_sec) + 1E-9 * 
int(mtim.c_tv_nsec)
-        ctim = st.c_st_ctim; ctime = int(ctim.c_tv_sec) + 1E-9 * 
int(ctim.c_tv_nsec)
+        atim = st.c_st_atim
+        mtim = st.c_st_mtim
+        ctim = st.c_st_ctim
+        atime, extra_atime = atim.c_tv_sec, int(atim.c_tv_nsec)
+        mtime, extra_mtime = mtim.c_tv_sec, int(mtim.c_tv_nsec)
+        ctime, extra_ctime = ctim.c_tv_sec, int(ctim.c_tv_nsec)
     else:
-        atime = st.c_st_atime
-        mtime = st.c_st_mtime
-        ctime = st.c_st_ctime
+        atime, extra_atime = st.c_st_atime, 0
+        mtime, extra_mtime = st.c_st_mtime, 0
+        ctime, extra_ctime = st.c_st_ctime, 0
 
     result = (st.c_st_mode,
               st.c_st_ino,
@@ -395,6 +477,10 @@
     if "st_rdev"    in STAT_FIELD_TYPES: result += (st.c_st_rdev,)
     if "st_flags"   in STAT_FIELD_TYPES: result += (st.c_st_flags,)
 
+    result += (extra_atime,
+               extra_mtime,
+               extra_ctime)
+
     return make_stat_result(result)
 
 
@@ -455,12 +541,14 @@
             # console or LPT device
             return make_stat_result((win32traits._S_IFCHR,
                                      0, 0, 0, 0, 0,
-                                     0, 0, 0, 0))
+                                     0, 0, 0, 0,
+                                     0, 0, 0))
         elif filetype == win32traits.FILE_TYPE_PIPE:
             # socket or named pipe
             return make_stat_result((win32traits._S_IFIFO,
                                      0, 0, 0, 0, 0,
-                                     0, 0, 0, 0))
+                                     0, 0, 0, 0,
+                                     0, 0, 0))
         elif filetype == win32traits.FILE_TYPE_UNKNOWN:
             error = rwin32.GetLastError_saved()
             if error != 0:
@@ -539,14 +627,11 @@
 #__________________________________________________
 # Helper functions for win32
 if _WIN32:
-    from rpython.rlib.rwin32file import FILE_TIME_to_time_t_float
+    from rpython.rlib.rwin32file import FILE_TIME_to_time_t_nsec
 
     def make_longlong(high, low):
         return (rffi.r_longlong(high) << 32) + rffi.r_longlong(low)
 
-    # Seconds between 1.1.1601 and 1.1.1970
-    secs_between_epochs = rffi.r_longlong(11644473600)
-
     @specialize.arg(0)
     def win32_xstat(traits, path, traverse=False):
         win32traits = make_win32_traits(traits)
@@ -582,14 +667,15 @@
     def win32_attribute_data_to_stat(win32traits, info):
         st_mode = win32_attributes_to_mode(win32traits, 
info.c_dwFileAttributes)
         st_size = make_longlong(info.c_nFileSizeHigh, info.c_nFileSizeLow)
-        ctime = FILE_TIME_to_time_t_float(info.c_ftCreationTime)
-        mtime = FILE_TIME_to_time_t_float(info.c_ftLastWriteTime)
-        atime = FILE_TIME_to_time_t_float(info.c_ftLastAccessTime)
+        ctime, extra_ctime = FILE_TIME_to_time_t_nsec(info.c_ftCreationTime)
+        mtime, extra_mtime = FILE_TIME_to_time_t_nsec(info.c_ftLastWriteTime)
+        atime, extra_atime = FILE_TIME_to_time_t_nsec(info.c_ftLastAccessTime)
 
         result = (st_mode,
                   0, 0, 0, 0, 0,
                   st_size,
-                  atime, mtime, ctime)
+                  atime, mtime, ctime,
+                  extra_atime, extra_mtime, extra_ctime)
 
         return make_stat_result(result)
 
@@ -597,9 +683,9 @@
         # similar to the one above
         st_mode = win32_attributes_to_mode(win32traits, 
info.c_dwFileAttributes)
         st_size = make_longlong(info.c_nFileSizeHigh, info.c_nFileSizeLow)
-        ctime = FILE_TIME_to_time_t_float(info.c_ftCreationTime)
-        mtime = FILE_TIME_to_time_t_float(info.c_ftLastWriteTime)
-        atime = FILE_TIME_to_time_t_float(info.c_ftLastAccessTime)
+        ctime, extra_ctime = FILE_TIME_to_time_t_nsec(info.c_ftCreationTime)
+        mtime, extra_mtime = FILE_TIME_to_time_t_nsec(info.c_ftLastWriteTime)
+        atime, extra_atime = FILE_TIME_to_time_t_nsec(info.c_ftLastAccessTime)
 
         # specific to fstat()
         st_ino = make_longlong(info.c_nFileIndexHigh, info.c_nFileIndexLow)
@@ -608,7 +694,8 @@
         result = (st_mode,
                   st_ino, 0, st_nlink, 0, 0,
                   st_size,
-                  atime, mtime, ctime)
+                  atime, mtime, ctime,
+                  extra_atime, extra_mtime, extra_ctime)
 
         return make_stat_result(result)
 
diff --git a/rpython/rlib/rwin32file.py b/rpython/rlib/rwin32file.py
--- a/rpython/rlib/rwin32file.py
+++ b/rpython/rlib/rwin32file.py
@@ -6,6 +6,7 @@
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 from rpython.rtyper.tool import rffi_platform as platform
 from rpython.rlib.objectmodel import specialize
+from rpython.rlib.rarithmetic import intmask
 
 @specialize.memo()
 def make_win32_traits(traits):
@@ -213,13 +214,25 @@
     return (rffi.r_longlong(high) << 32) + rffi.r_longlong(low)
 
 # Seconds between 1.1.1601 and 1.1.1970
-secs_between_epochs = rffi.r_longlong(11644473600)
+secs_between_epochs = 11644473600.0
+hns_between_epochs = rffi.r_longlong(116444736000000000)  # units of 100 nsec
 
 def FILE_TIME_to_time_t_float(filetime):
     ft = make_longlong(filetime.c_dwHighDateTime, filetime.c_dwLowDateTime)
     # FILETIME is in units of 100 nsec
     return float(ft) * (1.0 / 10000000.0) - secs_between_epochs
 
+def FILE_TIME_to_time_t_nsec(filetime):
+    """Like the previous function, but returns a pair: (integer part
+    'time_t' as a r_longlong, fractional part as an int measured in
+    nanoseconds).
+    """
+    ft = make_longlong(filetime.c_dwHighDateTime, filetime.c_dwLowDateTime)
+    ft -= hns_between_epochs
+    int_part = ft / 10000000
+    frac_part = ft - (int_part * 10000000)
+    return (int_part, intmask(frac_part) * 100)
+
 def time_t_to_FILE_TIME(time, filetime):
     ft = rffi.r_longlong((time + secs_between_epochs) * 10000000)
     filetime.c_dwHighDateTime = rffi.r_uint(ft >> 32)
diff --git a/rpython/rlib/test/test_rposix_stat.py 
b/rpython/rlib/test/test_rposix_stat.py
--- a/rpython/rlib/test/test_rposix_stat.py
+++ b/rpython/rlib/test/test_rposix_stat.py
@@ -2,12 +2,20 @@
 import py
 from rpython.rlib import rposix_stat
 from rpython.tool.udir import udir
+from rpython.translator.c.test.test_genc import compile
+from rpython.rtyper.lltypesystem import lltype
+
 
 class TestPosixStatFunctions:
     @py.test.mark.skipif("sys.platform == 'win32'",
                          reason="win32 only has the portable fields")
     def test_has_all_fields(self):
-        assert rposix_stat.STAT_FIELDS == rposix_stat.ALL_STAT_FIELDS[:13]
+        # XXX this test is obscure!  it will fail if the exact set of
+        # XXX stat fields found differs from the one we expect on Linux.
+        # XXX Why?
+        assert rposix_stat.STAT_FIELDS == (
+            rposix_stat.ALL_STAT_FIELDS[:13] +
+            rposix_stat.ALL_STAT_FIELDS[-3:])
 
     def test_stat(self):
         def check(f):
@@ -66,3 +74,27 @@
     finally:
         os.close(dirfd)
     assert result.st_atime == tmpdir.join('file').atime()
+
+def test_high_precision_stat_time():
+    def f():
+        st = os.stat('.')
+        # should be supported on all platforms, but give a result whose
+        # precision might be lower than full nanosecond
+        highprec = rposix_stat.get_stat_ns_as_bigint(st, "ctime")
+        return '%s;%s' % (st.st_ctime, highprec.str())
+    fc = compile(f, [])
+    as_string = fc()
+    asfloat, highprec = as_string.split(';')
+    asfloat = float(asfloat)
+    highprec = int(highprec)
+    st = os.stat('.')
+    assert abs(asfloat - st.st_ctime) < 500e-9
+    assert abs(highprec - int(st.st_ctime * 1e9)) < 500
+    assert abs(rposix_stat.get_stat_ns_as_bigint(st, "ctime").tolong()
+               - st.st_ctime * 1e9) < 3
+    if rposix_stat.TIMESPEC is not None:
+        with lltype.scoped_alloc(rposix_stat.STAT_STRUCT.TO) as stresult:
+            rposix_stat.c_stat(".", stresult)
+            assert 0 <= stresult.c_st_ctim.c_tv_nsec <= 999999999
+            assert highprec == (int(stresult.c_st_ctim.c_tv_sec) * 1000000000
+                                + int(stresult.c_st_ctim.c_tv_nsec))
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to