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