Author: Amaury Forgeot d'Arc <amaur...@gmail.com> Branch: more-rposix Changeset: r76721:8ff6cf17a905 Date: 2015-04-06 13:42 +0200 http://bitbucket.org/pypy/pypy/changeset/8ff6cf17a905/
Log: Move ll_os_stat to rposix_stat. Not tested at all on Windows. diff --git a/pypy/module/_io/interp_io.py b/pypy/module/_io/interp_io.py --- a/pypy/module/_io/interp_io.py +++ b/pypy/module/_io/interp_io.py @@ -7,7 +7,9 @@ from pypy.module.exceptions.interp_exceptions import W_IOError from pypy.module._io.interp_fileio import W_FileIO from pypy.module._io.interp_textio import W_TextIOWrapper -from rpython.rtyper.module.ll_os_stat import STAT_FIELD_TYPES +from rpython.rlib.rposix_stat import STAT_FIELD_TYPES + +HAS_BLKSIZE = 'st_blksize' in STAT_FIELD_TYPES class Cache: @@ -118,7 +120,7 @@ if buffering < 0: buffering = DEFAULT_BUFFER_SIZE - if space.config.translation.type_system == 'lltype' and 'st_blksize' in STAT_FIELD_TYPES: + if HAS_BLKSIZE: fileno = space.c_int_w(space.call_method(w_raw, "fileno")) try: st = os.fstat(fileno) 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 @@ -1,11 +1,11 @@ import os import sys -from rpython.rlib import rposix, objectmodel, rurandom +from rpython.rlib import rposix, rposix_stat +from rpython.rlib import objectmodel, rurandom from rpython.rlib.objectmodel import specialize from rpython.rlib.rarithmetic import r_longlong from rpython.rlib.unroll import unrolling_iterable -from rpython.rtyper.module import ll_os_stat from pypy.interpreter.gateway import unwrap_spec from pypy.interpreter.error import OperationError, wrap_oserror, wrap_oserror2 @@ -215,13 +215,13 @@ # ____________________________________________________________ -STAT_FIELDS = unrolling_iterable(enumerate(ll_os_stat.STAT_FIELDS)) +STAT_FIELDS = unrolling_iterable(enumerate(rposix_stat.STAT_FIELDS)) -STATVFS_FIELDS = unrolling_iterable(enumerate(ll_os_stat.STATVFS_FIELDS)) +STATVFS_FIELDS = unrolling_iterable(enumerate(rposix_stat.STATVFS_FIELDS)) def build_stat_result(space, st): FIELDS = STAT_FIELDS # also when not translating at all - lst = [None] * ll_os_stat.N_INDEXABLE_FIELDS + lst = [None] * rposix_stat.N_INDEXABLE_FIELDS w_keywords = space.newdict() stat_float_times = space.fromcache(StatState).stat_float_times for i, (name, TYPE) in FIELDS: @@ -229,7 +229,7 @@ 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 < ll_os_stat.N_INDEXABLE_FIELDS: + if i < rposix_stat.N_INDEXABLE_FIELDS: lst[i] = w_value else: space.setitem(w_keywords, space.wrap(name), w_value) @@ -257,7 +257,7 @@ def build_statvfs_result(space, st): - vals_w = [None] * len(ll_os_stat.STATVFS_FIELDS) + vals_w = [None] * len(rposix_stat.STATVFS_FIELDS) for i, (name, _) in STATVFS_FIELDS: vals_w[i] = space.wrap(getattr(st, name)) w_tuple = space.newtuple(vals_w) @@ -270,7 +270,7 @@ """Perform a stat system call on the file referenced to by an open file descriptor.""" try: - st = os.fstat(fd) + st = rposix_stat.fstat(fd) except OSError, e: raise wrap_oserror(space, e) else: @@ -292,7 +292,7 @@ """ try: - st = dispatch_filename(rposix.stat)(space, w_path) + st = dispatch_filename(rposix_stat.stat)(space, w_path) except OSError, e: raise wrap_oserror2(space, e, w_path) else: @@ -301,7 +301,7 @@ def lstat(space, w_path): "Like stat(path), but do no follow symbolic links." try: - st = dispatch_filename(rposix.lstat)(space, w_path) + st = dispatch_filename(rposix_stat.lstat)(space, w_path) except OSError, e: raise wrap_oserror2(space, e, w_path) else: @@ -330,7 +330,7 @@ @unwrap_spec(fd=c_int) def fstatvfs(space, fd): try: - st = os.fstatvfs(fd) + st = rposix_stat.fstatvfs(fd) except OSError as e: raise wrap_oserror(space, e) else: @@ -339,7 +339,7 @@ def statvfs(space, w_path): try: - st = dispatch_filename(rposix.statvfs)(space, w_path) + st = dispatch_filename(rposix_stat.statvfs)(space, w_path) except OSError as e: raise wrap_oserror2(space, e, w_path) else: diff --git a/rpython/rlib/_rposix_repr.py b/rpython/rlib/_rposix_repr.py new file mode 100644 --- /dev/null +++ b/rpython/rlib/_rposix_repr.py @@ -0,0 +1,122 @@ +""" +RTyping support for os.stat_result objects. +They are rtyped just like a tuple of the correct length supporting +only indexing and the st_xxx attributes. We need a custom StatResultRepr +because when rtyping for LL backends we have extra platform-dependent +items at the end of the tuple, but for OO backends we only want the +portable items. This allows the OO backends to assume a fixed shape for +the tuples returned by os.stat(). +""" +from rpython.annotator import model as annmodel +from rpython.rtyper.llannotation import lltype_to_annotation +from rpython.flowspace.model import Constant +from rpython.flowspace.operation import op +from rpython.tool.pairtype import pairtype +from rpython.rtyper.rmodel import Repr +from rpython.rtyper.rint import IntegerRepr +from rpython.rtyper.error import TyperError +from rpython.rlib import rposix_stat + + +class StatResultRepr(Repr): + + def __init__(self, rtyper): + self.rtyper = rtyper + self.stat_fields = rposix_stat.STAT_FIELDS + + self.stat_field_indexes = {} + for i, (name, TYPE) in enumerate(self.stat_fields): + self.stat_field_indexes[name] = i + + self.s_tuple = annmodel.SomeTuple([lltype_to_annotation(TYPE) + for name, TYPE in self.stat_fields]) + self.r_tuple = rtyper.getrepr(self.s_tuple) + self.lowleveltype = self.r_tuple.lowleveltype + + def redispatch_getfield(self, hop, index): + rtyper = self.rtyper + s_index = rtyper.annotator.bookkeeper.immutablevalue(index) + hop2 = hop.copy() + spaceop = op.getitem(hop.args_v[0], Constant(index)) + spaceop.result = hop.spaceop.result + hop2.spaceop = spaceop + hop2.args_v = spaceop.args + hop2.args_s = [self.s_tuple, s_index] + hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)] + return hop2.dispatch() + + def rtype_getattr(self, hop): + s_attr = hop.args_s[1] + attr = s_attr.const + try: + index = self.stat_field_indexes[attr] + except KeyError: + raise TyperError("os.stat().%s: field not available" % (attr,)) + return self.redispatch_getfield(hop, index) + + +class __extend__(pairtype(StatResultRepr, IntegerRepr)): + + def rtype_getitem((r_sta, r_int), hop): + s_int = hop.args_s[1] + index = s_int.const + return r_sta.redispatch_getfield(hop, index) + + +def specialize_make_stat_result(hop): + r_StatResult = hop.rtyper.getrepr(rposix_stat.s_StatResult) + [v_result] = hop.inputargs(r_StatResult.r_tuple) + # no-op conversion from r_StatResult.r_tuple to r_StatResult + hop.exception_cannot_occur() + return v_result + + +class StatvfsResultRepr(Repr): + + def __init__(self, rtyper): + self.rtyper = rtyper + self.statvfs_fields = rposix_stat.STATVFS_FIELDS + + self.statvfs_field_indexes = {} + for i, (name, TYPE) in enumerate(self.statvfs_fields): + self.statvfs_field_indexes[name] = i + + self.s_tuple = annmodel.SomeTuple([lltype_to_annotation(TYPE) + for name, TYPE in self.statvfs_fields]) + self.r_tuple = rtyper.getrepr(self.s_tuple) + self.lowleveltype = self.r_tuple.lowleveltype + + def redispatch_getfield(self, hop, index): + rtyper = self.rtyper + s_index = rtyper.annotator.bookkeeper.immutablevalue(index) + hop2 = hop.copy() + spaceop = op.getitem(hop.args_v[0], Constant(index)) + spaceop.result = hop.spaceop.result + hop2.spaceop = spaceop + hop2.args_v = spaceop.args + hop2.args_s = [self.s_tuple, s_index] + hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)] + return hop2.dispatch() + + def rtype_getattr(self, hop): + s_attr = hop.args_s[1] + attr = s_attr.const + try: + index = self.statvfs_field_indexes[attr] + except KeyError: + raise TyperError("os.statvfs().%s: field not available" % (attr,)) + return self.redispatch_getfield(hop, index) + + +class __extend__(pairtype(StatvfsResultRepr, IntegerRepr)): + def rtype_getitem((r_sta, r_int), hop): + s_int = hop.args_s[1] + index = s_int.const + return r_sta.redispatch_getfield(hop, index) + + +def specialize_make_statvfs_result(hop): + r_StatvfsResult = hop.rtyper.getrepr(rposix_stat.s_StatvfsResult) + [v_result] = hop.inputargs(r_StatvfsResult.r_tuple) + hop.exception_cannot_occur() + return v_result diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -534,7 +534,7 @@ else: r_int64 = int -# needed for ll_os_stat.time_t_to_FILE_TIME in the 64 bit case +# needed for rposix_stat.time_t_to_FILE_TIME in the 64 bit case r_uint32 = build_int('r_uint32', False, 32) SHRT_MIN = -2**(_get_bitsize('h') - 1) diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -355,18 +355,6 @@ def _preferred_traits(path): return string_traits -@specialize.argtype(0) -def stat(path): - return os.stat(_as_bytes(path)) - -@specialize.argtype(0) -def lstat(path): - return os.lstat(_as_bytes(path)) - -@specialize.argtype(0) -def statvfs(path): - return os.statvfs(_as_bytes(path)) - @specialize.argtype(0, 1) def putenv(name, value): os.environ[_as_bytes(name)] = _as_bytes(value) @@ -1284,7 +1272,7 @@ if not _WIN32: l_tmsbuf = lltype.malloc(TMSP.TO, flavor='raw') try: - handle_posix_error('times', c_times(l_tmsbuf)) + result = handle_posix_error('times', c_times(l_tmsbuf)) return ( rffi.cast(lltype.Signed, l_tmsbuf.c_tms_utime) / CLOCK_TICKS_PER_SECOND, diff --git a/rpython/rlib/rposix_stat.py b/rpython/rlib/rposix_stat.py new file mode 100644 --- /dev/null +++ b/rpython/rlib/rposix_stat.py @@ -0,0 +1,523 @@ +"""Annotation and rtyping support for the result of os.stat(), os.lstat() +and os.fstat(). In RPython like in plain Python the stat result can be +indexed like a tuple but also exposes the st_xxx attributes. +""" + +import os, sys + +from rpython.annotator import model as annmodel +from rpython.rtyper import extregistry +from rpython.tool.pairtype import pairtype +from rpython.rtyper.tool import rffi_platform as platform +from rpython.rtyper.llannotation import lltype_to_annotation + +from rpython.rlib.objectmodel import ( + specialize) +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.translator.tool.cbuild import ExternalCompilationInfo +from rpython.rlib.rposix import ( + replace_os_function, handle_posix_error, _as_bytes0) + +_WIN32 = sys.platform.startswith('win') +_LINUX = sys.platform.startswith('linux') + +# Support for float times is here. +# - ALL_STAT_FIELDS contains Float fields if the system can retrieve +# sub-second timestamps. +# - TIMESPEC is defined when the "struct stat" contains st_atim field. + +if sys.platform.startswith('linux') or sys.platform.startswith('openbsd'): + TIMESPEC = platform.Struct('struct timespec', + [('tv_sec', rffi.TIME_T), + ('tv_nsec', rffi.LONG)]) +else: + TIMESPEC = None + +# all possible fields - some of them are not available on all platforms +ALL_STAT_FIELDS = [ + ("st_mode", lltype.Signed), + ("st_ino", lltype.SignedLongLong), + ("st_dev", lltype.SignedLongLong), + ("st_nlink", lltype.Signed), + ("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_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 +] +N_INDEXABLE_FIELDS = 10 + +# For OO backends, expose only the portable fields (the first 10). +PORTABLE_STAT_FIELDS = ALL_STAT_FIELDS[:N_INDEXABLE_FIELDS] + +STATVFS_FIELDS = [ + ("f_bsize", lltype.Signed), + ("f_frsize", lltype.Signed), + ("f_blocks", lltype.Signed), + ("f_bfree", lltype.Signed), + ("f_bavail", lltype.Signed), + ("f_files", lltype.Signed), + ("f_ffree", lltype.Signed), + ("f_favail", lltype.Signed), + ("f_flag", lltype.Signed), + ("f_namemax", lltype.Signed), +] + + +# ____________________________________________________________ +# +# Annotation support + +class SomeStatResult(annmodel.SomeObject): + knowntype = os.stat_result + + def rtyper_makerepr(self, rtyper): + from rpython.rlib import _rposix_repr + return _rposix_repr.StatResultRepr(rtyper) + + def rtyper_makekey(self): + return self.__class__, + + def getattr(self, s_attr): + assert s_attr.is_constant(), "non-constant attr name in getattr()" + attrname = s_attr.const + TYPE = STAT_FIELD_TYPES[attrname] + return lltype_to_annotation(TYPE) + + def _get_rmarshall_support_(self): # for rlib.rmarshal + # reduce and recreate stat_result objects from 10-tuples + # (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]) + + def stat_result_recreate(tup): + return make_stat_result(tup + extra_zeroes) + 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)) + return s_reduced, stat_result_reduce, stat_result_recreate + + +class SomeStatvfsResult(annmodel.SomeObject): + if hasattr(os, 'statvfs_result'): + knowntype = os.statvfs_result + else: + knowntype = None # will not be used + + def rtyper_makerepr(self, rtyper): + from rpython.rlib import _rposix_repr + return _rposix_repr.StatvfsResultRepr(rtyper) + + def rtyper_makekey(self): + return self.__class__, + + def getattr(self, s_attr): + assert s_attr.is_constant() + TYPE = STATVFS_FIELD_TYPES[s_attr.const] + return lltype_to_annotation(TYPE) + + +class __extend__(pairtype(SomeStatResult, annmodel.SomeInteger)): + 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" + name, TYPE = STAT_FIELDS[index] + return lltype_to_annotation(TYPE) + + +class __extend__(pairtype(SomeStatvfsResult, annmodel.SomeInteger)): + def getitem((s_stat, s_int)): + assert s_int.is_constant() + name, TYPE = STATVFS_FIELDS[s_int.const] + return lltype_to_annotation(TYPE) + + +s_StatResult = SomeStatResult() +s_StatvfsResult = SomeStatvfsResult() + + +def make_stat_result(tup): + """Turn a tuple into an os.stat_result object.""" + positional = tup[:N_INDEXABLE_FIELDS] + kwds = {} + for i, name in enumerate(STAT_FIELD_NAMES[N_INDEXABLE_FIELDS:]): + kwds[name] = tup[N_INDEXABLE_FIELDS + i] + return os.stat_result(positional, kwds) + + +def make_statvfs_result(tup): + return os.statvfs_result(tup) + + +class MakeStatResultEntry(extregistry.ExtRegistryEntry): + _about_ = make_stat_result + + def compute_result_annotation(self, s_tup): + return s_StatResult + + def specialize_call(self, hop): + from rpython.rlib import _rposix_repr + return _rposix_repr.specialize_make_stat_result(hop) + + +class MakeStatvfsResultEntry(extregistry.ExtRegistryEntry): + _about_ = make_statvfs_result + + def compute_result_annotation(self, s_tup): + return s_StatvfsResult + + def specialize_call(self, hop): + from rpython.rlib import _rposix_repr + return _rposix_repr.specialize_make_statvfs_result(hop) + +# ____________________________________________________________ +# +# RFFI support + +if sys.platform.startswith('win'): + _name_struct_stat = '_stati64' + INCLUDES = ['sys/types.h', 'sys/stat.h', 'sys/statvfs.h'] +else: + if _LINUX: + _name_struct_stat = 'stat64' + else: + _name_struct_stat = 'stat' + INCLUDES = ['sys/types.h', 'sys/stat.h', 'sys/statvfs.h', 'unistd.h'] + +compilation_info = ExternalCompilationInfo( + # This must be set to 64 on some systems to enable large file support. + #pre_include_bits = ['#define _FILE_OFFSET_BITS 64'], + # ^^^ nowadays it's always set in all C files we produce. + includes=INCLUDES +) + +if TIMESPEC is not None: + class CConfig_for_timespec: + _compilation_info_ = compilation_info + TIMESPEC = TIMESPEC + TIMESPEC = lltype.Ptr( + platform.configure(CConfig_for_timespec)['TIMESPEC']) + + +def posix_declaration(try_to_add=None): + global STAT_STRUCT, STATVFS_STRUCT + + LL_STAT_FIELDS = STAT_FIELDS[:] + if try_to_add: + LL_STAT_FIELDS.append(try_to_add) + + if TIMESPEC is not None: + + def _expand(lst, originalname, timespecname): + for i, (_name, _TYPE) in enumerate(lst): + if _name == originalname: + # replace the 'st_atime' field of type rffi.DOUBLE + # with a field 'st_atim' of type 'struct timespec' + lst[i] = (timespecname, TIMESPEC.TO) + break + + _expand(LL_STAT_FIELDS, 'st_atime', 'st_atim') + _expand(LL_STAT_FIELDS, 'st_mtime', 'st_mtim') + _expand(LL_STAT_FIELDS, 'st_ctime', 'st_ctim') + + del _expand + else: + # Replace float fields with integers + for name in ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime'): + for i, (_name, _TYPE) in enumerate(LL_STAT_FIELDS): + if _name == name: + LL_STAT_FIELDS[i] = (_name, lltype.Signed) + break + + class CConfig: + _compilation_info_ = compilation_info + STAT_STRUCT = platform.Struct('struct %s' % _name_struct_stat, LL_STAT_FIELDS) + STATVFS_STRUCT = platform.Struct('struct statvfs', STATVFS_FIELDS) + + try: + config = platform.configure(CConfig, ignore_errors=try_to_add is not None) + except platform.CompilationError: + if try_to_add: + return # failed to add this field, give up + raise + + STAT_STRUCT = lltype.Ptr(config['STAT_STRUCT']) + STATVFS_STRUCT = lltype.Ptr(config['STATVFS_STRUCT']) + if try_to_add: + STAT_FIELDS.append(try_to_add) + + +# This lists only the fields that have been found on the underlying platform. +# Initially only the PORTABLE_STAT_FIELDS, but more may be added by the +# following loop. +STAT_FIELDS = PORTABLE_STAT_FIELDS[:] + +if sys.platform != 'win32': + posix_declaration() + for _i in range(len(PORTABLE_STAT_FIELDS), len(ALL_STAT_FIELDS)): + posix_declaration(ALL_STAT_FIELDS[_i]) + del _i + +# 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] +del _name, _TYPE + +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) + else: + atime = st.c_st_atime + mtime = st.c_st_mtime + ctime = st.c_st_ctime + + result = (st.c_st_mode, + st.c_st_ino, + st.c_st_dev, + st.c_st_nlink, + st.c_st_uid, + st.c_st_gid, + st.c_st_size, + atime, + mtime, + ctime) + + if "st_blksize" in STAT_FIELD_TYPES: result += (st.c_st_blksize,) + if "st_blocks" in STAT_FIELD_TYPES: result += (st.c_st_blocks,) + if "st_rdev" in STAT_FIELD_TYPES: result += (st.c_st_rdev,) + if "st_flags" in STAT_FIELD_TYPES: result += (st.c_st_flags,) + + return make_stat_result(result) + + +def build_statvfs_result(st): + return make_statvfs_result(( + st.c_f_bsize, + st.c_f_frsize, + st.c_f_blocks, + st.c_f_bfree, + st.c_f_bavail, + st.c_f_files, + st.c_f_ffree, + st.c_f_favail, + st.c_f_flag, + st.c_f_namemax + )) + + +# Implement and register os.stat() & variants + +c_fstat = rffi.llexternal('fstat64' if _LINUX else 'fstat', + [rffi.INT, STAT_STRUCT], rffi.INT, + compilation_info=compilation_info, + save_err=rffi.RFFI_SAVE_ERRNO, + macro=True) +c_stat = rffi.llexternal('stat64' if _LINUX else 'stat', + [rffi.CCHARP, STAT_STRUCT], rffi.INT, + compilation_info=compilation_info, + save_err=rffi.RFFI_SAVE_ERRNO, + macro=True) +c_lstat = rffi.llexternal('lstat64' if _LINUX else 'lstat', + [rffi.CCHARP, STAT_STRUCT], rffi.INT, + compilation_info=compilation_info, + save_err=rffi.RFFI_SAVE_ERRNO, + macro=True) + +c_fstatvfs = rffi.llexternal('fstatvfs', + [rffi.INT, STATVFS_STRUCT], rffi.INT, + compilation_info=compilation_info, + save_err=rffi.RFFI_SAVE_ERRNO) +c_statvfs = rffi.llexternal('statvfs', + [rffi.CCHARP, STATVFS_STRUCT], rffi.INT, + compilation_info=compilation_info, + save_err=rffi.RFFI_SAVE_ERRNO) + +@replace_os_function('fstat') +def fstat(fd): + with lltype.scoped_alloc(STAT_STRUCT.TO) as stresult: + if not _WIN32: + handle_posix_error('fstat', c_fstat(fd, stresult)) + return build_stat_result(stresult) + else: + handle = rwin32.get_osfhandle(fd) + filetype = win32traits.GetFileType(handle) + if filetype == win32traits.FILE_TYPE_CHAR: + # console or LPT device + return make_stat_result((win32traits._S_IFCHR, + 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)) + elif filetype == win32traits.FILE_TYPE_UNKNOWN: + error = rwin32.GetLastError_saved() + if error != 0: + raise WindowsError(error, "os_fstat failed") + # else: unknown but valid file + + # normal disk file (FILE_TYPE_DISK) + info = lltype.malloc(win32traits.BY_HANDLE_FILE_INFORMATION, + flavor='raw', zero=True) + try: + res = win32traits.GetFileInformationByHandle(handle, info) + if res == 0: + raise WindowsError(rwin32.GetLastError_saved(), + "os_fstat failed") + return win32_by_handle_info_to_stat(info) + finally: + lltype.free(info, flavor='raw') + +@replace_os_function('stat') +@specialize.argtype(0) +def stat(path): + with lltype.scoped_alloc(STAT_STRUCT.TO) as stresult: + if not _WIN32: + arg = _as_bytes0(path) + handle_posix_error('stat', c_stat(arg, stresult)) + return build_stat_result(stresult) + else: + traits = _preferred_traits(path) + path = traits._as_str0(path) + return win32_xstat(path, traverse=True) + +@replace_os_function('lstat') +@specialize.argtype(0) +def lstat(path): + with lltype.scoped_alloc(STAT_STRUCT.TO) as stresult: + if not _WIN32: + arg = _as_bytes0(path) + handle_posix_error('lstat', c_stat(arg, stresult)) + else: + traits = _preferred_traits(path) + arg = traits._as_str0(path) + handle_posix_error('lstat', c_stat(arg, stresult)) + return build_stat_result(stresult) + +@replace_os_function('fstatvfs') +def fstatvfs(fd): + with lltype.scoped_alloc(STATVFS_STRUCT.TO) as stresult: + handle_posix_error('fstatvfs', c_fstatvfs(fd, stresult)) + return build_statvfs_result(stresult) + +@replace_os_function('statvfs') +def statvfs(path): + with lltype.scoped_alloc(STATVFS_STRUCT.TO) as stresult: + arg = _as_bytes0(path) + handle_posix_error('statvfs', c_statvfs(arg, stresult)) + return build_statvfs_result(stresult) + +#__________________________________________________ +# Helper functions for win32 +if _WIN32: + + 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) + + 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 time_t_to_FILE_TIME(time, filetime): + ft = rffi.r_longlong((time + secs_between_epochs) * 10000000) + filetime.c_dwHighDateTime = rffi.r_uint(ft >> 32) + filetime.c_dwLowDateTime = rffi.r_uint(ft) # masking off high bits + + def win32_xstat(traits, path, traverse=False): + win32traits = make_win32_traits(traits) + with lltype.scoped_alloc( + win32traits.WIN32_FILE_ATTRIBUTE_DATA) as data: + res = win32traits.GetFileAttributesEx( + path, win32traits.GetFileExInfoStandard, data) + if res == 0: + errcode = rwin32.GetLastError_saved() + if errcode == win32traits.ERROR_SHARING_VIOLATION: + res = win32_attributes_from_dir(path, data) + if res == 0: + errcode = rwin32.GetLastError_saved() + raise WindowsError(errcode, "os_stat failed") + return attribute_data_to_stat(data) + + def win32_attributes_to_mode(attributes): + m = 0 + attributes = intmask(attributes) + if attributes & win32traits.FILE_ATTRIBUTE_DIRECTORY: + m |= win32traits._S_IFDIR | 0111 # IFEXEC for user,group,other + else: + m |= win32traits._S_IFREG + if attributes & win32traits.FILE_ATTRIBUTE_READONLY: + m |= 0444 + else: + m |= 0666 + return m + + def win32_attribute_data_to_stat(info): + st_mode = win32_attributes_to_mode(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) + + result = (st_mode, + 0, 0, 0, 0, 0, + st_size, + atime, mtime, ctime) + + return make_stat_result(result) + + def win32_by_handle_info_to_stat(info): + # similar to the one above + st_mode = win32_attributes_to_mode(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) + + # specific to fstat() + st_ino = make_longlong(info.c_nFileIndexHigh, info.c_nFileIndexLow) + st_nlink = info.c_nNumberOfLinks + + result = (st_mode, + st_ino, 0, st_nlink, 0, 0, + st_size, + atime, mtime, ctime) + + return make_stat_result(result) + + def win32_attributes_from_dir(l_path, data): + filedata = lltype.malloc(win32traits.WIN32_FIND_DATA, flavor='raw') + try: + hFindFile = win32traits.FindFirstFile(l_path, filedata) + if hFindFile == rwin32.INVALID_HANDLE_VALUE: + return 0 + win32traits.FindClose(hFindFile) + data.c_dwFileAttributes = filedata.c_dwFileAttributes + rffi.structcopy(data.c_ftCreationTime, filedata.c_ftCreationTime) + rffi.structcopy(data.c_ftLastAccessTime, filedata.c_ftLastAccessTime) + rffi.structcopy(data.c_ftLastWriteTime, filedata.c_ftLastWriteTime) + data.c_nFileSizeHigh = filedata.c_nFileSizeHigh + data.c_nFileSizeLow = filedata.c_nFileSizeLow + return 1 + finally: + lltype.free(filedata, flavor='raw') + diff --git a/rpython/rlib/test/test_rmarshal.py b/rpython/rlib/test/test_rmarshal.py --- a/rpython/rlib/test/test_rmarshal.py +++ b/rpython/rlib/test/test_rmarshal.py @@ -167,7 +167,7 @@ def test_stat_result(): import os from rpython.translator.c.test.test_genc import compile - from rpython.rtyper.module.ll_os_stat import s_StatResult + from rpython.rlib.rposix_stat import s_StatResult marshal_stat_result = get_marshaller(s_StatResult) unmarshal_stat_result = get_unmarshaller(s_StatResult) def f(path): diff --git a/rpython/rlib/test/test_rposix.py b/rpython/rlib/test/test_rposix.py --- a/rpython/rlib/test/test_rposix.py +++ b/rpython/rlib/test/test_rposix.py @@ -1,7 +1,7 @@ from rpython.rtyper.test.test_llinterp import interpret from rpython.translator.c.test.test_genc import compile from rpython.tool.udir import udir -from rpython.rlib import rposix, rstring +from rpython.rlib import rposix, rposix_stat, rstring import os, sys import py @@ -60,7 +60,7 @@ def test_stat(self): def f(): - return rposix.stat(self.path).st_mtime + return rposix_stat.stat(self.path).st_mtime if sys.platform == 'win32': # double vs. float, be satisfied with sub-millisec resolution assert abs(interpret(f, []) - os.stat(self.ufilename).st_mtime) < 1e-4 diff --git a/rpython/rtyper/module/ll_os.py b/rpython/rtyper/module/ll_os.py --- a/rpython/rtyper/module/ll_os.py +++ b/rpython/rtyper/module/ll_os.py @@ -119,40 +119,6 @@ ) -class RegisterOs(BaseLazyRegistering): - - def __init__(self): - self.configure(CConfig) - -# --------------------------- os.stat & variants --------------------------- - - @registering(os.fstat) - def register_os_fstat(self): - from rpython.rtyper.module import ll_os_stat - return ll_os_stat.register_stat_variant('fstat', StringTraits()) - - @registering_str_unicode(os.stat) - def register_os_stat(self, traits): - from rpython.rtyper.module import ll_os_stat - return ll_os_stat.register_stat_variant('stat', traits) - - @registering_str_unicode(os.lstat) - def register_os_lstat(self, traits): - from rpython.rtyper.module import ll_os_stat - return ll_os_stat.register_stat_variant('lstat', traits) - - @registering_if(os, 'fstatvfs') - def register_os_fstatvfs(self): - from rpython.rtyper.module import ll_os_stat - return ll_os_stat.register_statvfs_variant('fstatvfs', StringTraits()) - - if hasattr(os, 'statvfs'): - @registering_str_unicode(os.statvfs) - def register_os_statvfs(self, traits): - from rpython.rtyper.module import ll_os_stat - return ll_os_stat.register_statvfs_variant('statvfs', traits) - - # ____________________________________________________________ # Support for os.environ diff --git a/rpython/rtyper/module/ll_os_stat.py b/rpython/rtyper/module/ll_os_stat.py deleted file mode 100644 --- a/rpython/rtyper/module/ll_os_stat.py +++ /dev/null @@ -1,592 +0,0 @@ -"""Annotation and rtyping support for the result of os.stat(), os.lstat() -and os.fstat(). In RPython like in plain Python the stat result can be -indexed like a tuple but also exposes the st_xxx attributes. -""" - -import os -import sys - -from rpython.annotator import model as annmodel -from rpython.rtyper.llannotation import lltype_to_annotation -from rpython.rlib import rposix -from rpython.rlib.rarithmetic import intmask -from rpython.rtyper import extregistry -from rpython.rtyper.annlowlevel import hlstr -from rpython.rtyper.extfunc import extdef -from rpython.rtyper.lltypesystem import rffi, lltype -from rpython.rtyper.rtuple import TUPLE_TYPE -from rpython.rtyper.tool import rffi_platform as platform -from rpython.tool.pairtype import pairtype -from rpython.tool.sourcetools import func_renamer -from rpython.translator.tool.cbuild import ExternalCompilationInfo - -# Support for float times is here. -# - ALL_STAT_FIELDS contains Float fields if the system can retrieve -# sub-second timestamps. -# - TIMESPEC is defined when the "struct stat" contains st_atim field. - -if sys.platform.startswith('linux') or sys.platform.startswith('openbsd'): - TIMESPEC = platform.Struct('struct timespec', - [('tv_sec', rffi.TIME_T), - ('tv_nsec', rffi.LONG)]) -else: - TIMESPEC = None - -# all possible fields - some of them are not available on all platforms -ALL_STAT_FIELDS = [ - ("st_mode", lltype.Signed), - ("st_ino", lltype.SignedLongLong), - ("st_dev", lltype.SignedLongLong), - ("st_nlink", lltype.Signed), - ("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_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 -] -N_INDEXABLE_FIELDS = 10 - -# For OO backends, expose only the portable fields (the first 10). -PORTABLE_STAT_FIELDS = ALL_STAT_FIELDS[:N_INDEXABLE_FIELDS] - -STATVFS_FIELDS = [ - ("f_bsize", lltype.Signed), - ("f_frsize", lltype.Signed), - ("f_blocks", lltype.Signed), - ("f_bfree", lltype.Signed), - ("f_bavail", lltype.Signed), - ("f_files", lltype.Signed), - ("f_ffree", lltype.Signed), - ("f_favail", lltype.Signed), - ("f_flag", lltype.Signed), - ("f_namemax", lltype.Signed), -] - - -# ____________________________________________________________ -# -# Annotation support - -class SomeStatResult(annmodel.SomeObject): - knowntype = os.stat_result - - def rtyper_makerepr(self, rtyper): - from rpython.rtyper.module import r_os_stat - return r_os_stat.StatResultRepr(rtyper) - - def rtyper_makekey(self): - return self.__class__, - - def getattr(self, s_attr): - assert s_attr.is_constant(), "non-constant attr name in getattr()" - attrname = s_attr.const - TYPE = STAT_FIELD_TYPES[attrname] - return lltype_to_annotation(TYPE) - - def _get_rmarshall_support_(self): # for rlib.rmarshal - # reduce and recreate stat_result objects from 10-tuples - # (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]) - - def stat_result_recreate(tup): - return make_stat_result(tup + extra_zeroes) - 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)) - return s_reduced, stat_result_reduce, stat_result_recreate - - -class SomeStatvfsResult(annmodel.SomeObject): - if hasattr(os, 'statvfs_result'): - knowntype = os.statvfs_result - else: - knowntype = None # will not be used - - def rtyper_makerepr(self, rtyper): - from rpython.rtyper.module import r_os_stat - return r_os_stat.StatvfsResultRepr(rtyper) - - def rtyper_makekey(self): - return self.__class__, - - def getattr(self, s_attr): - assert s_attr.is_constant() - TYPE = STATVFS_FIELD_TYPES[s_attr.const] - return lltype_to_annotation(TYPE) - - -class __extend__(pairtype(SomeStatResult, annmodel.SomeInteger)): - 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" - name, TYPE = STAT_FIELDS[index] - return lltype_to_annotation(TYPE) - - -class __extend__(pairtype(SomeStatvfsResult, annmodel.SomeInteger)): - def getitem((s_stat, s_int)): - assert s_int.is_constant() - name, TYPE = STATVFS_FIELDS[s_int.const] - return lltype_to_annotation(TYPE) - - -s_StatResult = SomeStatResult() -s_StatvfsResult = SomeStatvfsResult() - - -def make_stat_result(tup): - """Turn a tuple into an os.stat_result object.""" - positional = tup[:N_INDEXABLE_FIELDS] - kwds = {} - for i, name in enumerate(STAT_FIELD_NAMES[N_INDEXABLE_FIELDS:]): - kwds[name] = tup[N_INDEXABLE_FIELDS + i] - return os.stat_result(positional, kwds) - - -def make_statvfs_result(tup): - return os.statvfs_result(tup) - - -class MakeStatResultEntry(extregistry.ExtRegistryEntry): - _about_ = make_stat_result - - def compute_result_annotation(self, s_tup): - return s_StatResult - - def specialize_call(self, hop): - from rpython.rtyper.module import r_os_stat - return r_os_stat.specialize_make_stat_result(hop) - - -class MakeStatvfsResultEntry(extregistry.ExtRegistryEntry): - _about_ = make_statvfs_result - - def compute_result_annotation(self, s_tup): - return s_StatvfsResult - - def specialize_call(self, hop): - from rpython.rtyper.module import r_os_stat - return r_os_stat.specialize_make_statvfs_result(hop) - -# ____________________________________________________________ -# -# RFFI support - -if sys.platform.startswith('win'): - _name_struct_stat = '_stati64' - INCLUDES = ['sys/types.h', 'sys/stat.h', 'sys/statvfs.h'] -else: - if sys.platform.startswith('linux'): - _name_struct_stat = 'stat64' - else: - _name_struct_stat = 'stat' - INCLUDES = ['sys/types.h', 'sys/stat.h', 'sys/statvfs.h', 'unistd.h'] - -compilation_info = ExternalCompilationInfo( - # This must be set to 64 on some systems to enable large file support. - #pre_include_bits = ['#define _FILE_OFFSET_BITS 64'], - # ^^^ nowadays it's always set in all C files we produce. - includes=INCLUDES -) - -if TIMESPEC is not None: - class CConfig_for_timespec: - _compilation_info_ = compilation_info - TIMESPEC = TIMESPEC - TIMESPEC = lltype.Ptr( - platform.configure(CConfig_for_timespec)['TIMESPEC']) - - -def posix_declaration(try_to_add=None): - global STAT_STRUCT, STATVFS_STRUCT - - LL_STAT_FIELDS = STAT_FIELDS[:] - if try_to_add: - LL_STAT_FIELDS.append(try_to_add) - - if TIMESPEC is not None: - - def _expand(lst, originalname, timespecname): - for i, (_name, _TYPE) in enumerate(lst): - if _name == originalname: - # replace the 'st_atime' field of type rffi.DOUBLE - # with a field 'st_atim' of type 'struct timespec' - lst[i] = (timespecname, TIMESPEC.TO) - break - - _expand(LL_STAT_FIELDS, 'st_atime', 'st_atim') - _expand(LL_STAT_FIELDS, 'st_mtime', 'st_mtim') - _expand(LL_STAT_FIELDS, 'st_ctime', 'st_ctim') - - del _expand - else: - # Replace float fields with integers - for name in ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime'): - for i, (_name, _TYPE) in enumerate(LL_STAT_FIELDS): - if _name == name: - LL_STAT_FIELDS[i] = (_name, lltype.Signed) - break - - class CConfig: - _compilation_info_ = compilation_info - STAT_STRUCT = platform.Struct('struct %s' % _name_struct_stat, LL_STAT_FIELDS) - STATVFS_STRUCT = platform.Struct('struct statvfs', STATVFS_FIELDS) - - try: - config = platform.configure(CConfig, ignore_errors=try_to_add is not None) - except platform.CompilationError: - if try_to_add: - return # failed to add this field, give up - raise - - STAT_STRUCT = lltype.Ptr(config['STAT_STRUCT']) - STATVFS_STRUCT = lltype.Ptr(config['STATVFS_STRUCT']) - if try_to_add: - STAT_FIELDS.append(try_to_add) - - -# This lists only the fields that have been found on the underlying platform. -# Initially only the PORTABLE_STAT_FIELDS, but more may be added by the -# following loop. -STAT_FIELDS = PORTABLE_STAT_FIELDS[:] - -if sys.platform != 'win32': - posix_declaration() - for _i in range(len(PORTABLE_STAT_FIELDS), len(ALL_STAT_FIELDS)): - posix_declaration(ALL_STAT_FIELDS[_i]) - del _i - -# 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] -del _name, _TYPE - -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) - else: - atime = st.c_st_atime - mtime = st.c_st_mtime - ctime = st.c_st_ctime - - result = (st.c_st_mode, - st.c_st_ino, - st.c_st_dev, - st.c_st_nlink, - st.c_st_uid, - st.c_st_gid, - st.c_st_size, - atime, - mtime, - ctime) - - if "st_blksize" in STAT_FIELD_TYPES: result += (st.c_st_blksize,) - if "st_blocks" in STAT_FIELD_TYPES: result += (st.c_st_blocks,) - if "st_rdev" in STAT_FIELD_TYPES: result += (st.c_st_rdev,) - if "st_flags" in STAT_FIELD_TYPES: result += (st.c_st_flags,) - - return make_stat_result(result) - - -def build_statvfs_result(st): - return make_statvfs_result(( - st.c_f_bsize, - st.c_f_frsize, - st.c_f_blocks, - st.c_f_bfree, - st.c_f_bavail, - st.c_f_files, - st.c_f_ffree, - st.c_f_favail, - st.c_f_flag, - st.c_f_namemax - )) - - -def register_stat_variant(name, traits): - if name != 'fstat': - arg_is_path = True - s_arg = traits.str0 - ARG1 = traits.CCHARP - else: - arg_is_path = False - s_arg = int - ARG1 = rffi.INT - - if sys.platform == 'win32': - # See Win32 implementation below - posix_stat_llimpl = make_win32_stat_impl(name, traits) - - return extdef( - [s_arg], s_StatResult, traits.ll_os_name(name), - llimpl=posix_stat_llimpl) - - if sys.platform.startswith('linux'): - # because we always use _FILE_OFFSET_BITS 64 - this helps things work that are not a c compiler - _functions = {'stat': 'stat64', - 'fstat': 'fstat64', - 'lstat': 'lstat64'} - c_func_name = _functions[name] - else: - c_func_name = name - - posix_mystat = rffi.llexternal(c_func_name, - [ARG1, STAT_STRUCT], rffi.INT, - compilation_info=compilation_info, - save_err=rffi.RFFI_SAVE_ERRNO) - - @func_renamer('os_%s_llimpl' % (name,)) - def posix_stat_llimpl(arg): - stresult = lltype.malloc(STAT_STRUCT.TO, flavor='raw') - try: - if arg_is_path: - arg = traits.str2charp(arg) - error = rffi.cast(rffi.LONG, posix_mystat(arg, stresult)) - if arg_is_path: - traits.free_charp(arg) - if error != 0: - raise OSError(rposix.get_saved_errno(), "os_?stat failed") - return build_stat_result(stresult) - finally: - lltype.free(stresult, flavor='raw') - - @func_renamer('os_%s_fake' % (name,)) - def posix_fakeimpl(arg): - if s_arg == traits.str0: - arg = hlstr(arg) - st = getattr(os, name)(arg) - fields = [TYPE for fieldname, TYPE in STAT_FIELDS] - TP = TUPLE_TYPE(fields) - ll_tup = lltype.malloc(TP.TO) - for i, (fieldname, TYPE) in enumerate(STAT_FIELDS): - val = getattr(st, fieldname) - if isinstance(TYPE, lltype.Number): - rffi.setintfield(ll_tup, 'item%d' % i, int(val)) - elif TYPE is lltype.Float: - setattr(ll_tup, 'item%d' % i, float(val)) - else: - setattr(ll_tup, 'item%d' % i, val) - return ll_tup - - return extdef( - [s_arg], s_StatResult, "ll_os.ll_os_%s" % (name,), - llimpl=posix_stat_llimpl, llfakeimpl=posix_fakeimpl) - - -def register_statvfs_variant(name, traits): - if name != 'fstatvfs': - arg_is_path = True - s_arg = traits.str0 - ARG1 = traits.CCHARP - else: - arg_is_path = False - s_arg = int - ARG1 = rffi.INT - - posix_mystatvfs = rffi.llexternal(name, - [ARG1, STATVFS_STRUCT], rffi.INT, - compilation_info=compilation_info, - save_err=rffi.RFFI_SAVE_ERRNO) - - @func_renamer('os_%s_llimpl' % (name,)) - def posix_statvfs_llimpl(arg): - stresult = lltype.malloc(STATVFS_STRUCT.TO, flavor='raw') - try: - if arg_is_path: - arg = traits.str2charp(arg) - error = rffi.cast(rffi.LONG, posix_mystatvfs(arg, stresult)) - if arg_is_path: - traits.free_charp(arg) - if error != 0: - raise OSError(rposix.get_saved_errno(), "os_?statvfs failed") - return build_statvfs_result(stresult) - finally: - lltype.free(stresult, flavor='raw') - - @func_renamer('os_%s_fake' % (name,)) - def posix_fakeimpl(arg): - if s_arg == traits.str0: - arg = hlstr(arg) - st = getattr(os, name)(arg) - fields = [TYPE for fieldname, TYPE in STATVFS_FIELDS] - TP = TUPLE_TYPE(fields) - ll_tup = lltype.malloc(TP.TO) - for i, (fieldname, TYPE) in enumerate(STATVFS_FIELDS): - val = getattr(st, fieldname) - rffi.setintfield(ll_tup, 'item%d' % i, int(val)) - return ll_tup - - return extdef( - [s_arg], s_StatvfsResult, "ll_os.ll_os_%s" % (name,), - llimpl=posix_statvfs_llimpl, llfakeimpl=posix_fakeimpl - ) - - -def make_win32_stat_impl(name, traits): - from rpython.rlib import rwin32 - from rpython.rlib.rwin32file import make_win32_traits - win32traits = make_win32_traits(traits) - - # The CRT of Windows has a number of flaws wrt. its stat() implementation: - # - time stamps are restricted to second resolution - # - file modification times suffer from forth-and-back conversions between - # UTC and local time - # Therefore, we implement our own stat, based on the Win32 API directly. - from rpython.rtyper.tool import rffi_platform as platform - from rpython.translator.tool.cbuild import ExternalCompilationInfo - from rpython.rlib import rwin32 - - assert len(STAT_FIELDS) == 10 # no extra fields on Windows - - def attributes_to_mode(attributes): - m = 0 - attributes = intmask(attributes) - if attributes & win32traits.FILE_ATTRIBUTE_DIRECTORY: - m |= win32traits._S_IFDIR | 0111 # IFEXEC for user,group,other - else: - m |= win32traits._S_IFREG - if attributes & win32traits.FILE_ATTRIBUTE_READONLY: - m |= 0444 - else: - m |= 0666 - return m - - def attribute_data_to_stat(info): - st_mode = attributes_to_mode(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) - - result = (st_mode, - 0, 0, 0, 0, 0, - st_size, - atime, mtime, ctime) - - return make_stat_result(result) - - def by_handle_info_to_stat(info): - # similar to the one above - st_mode = attributes_to_mode(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) - - # specific to fstat() - st_ino = make_longlong(info.c_nFileIndexHigh, info.c_nFileIndexLow) - st_nlink = info.c_nNumberOfLinks - - result = (st_mode, - st_ino, 0, st_nlink, 0, 0, - st_size, - atime, mtime, ctime) - - return make_stat_result(result) - - def attributes_from_dir(l_path, data): - filedata = lltype.malloc(win32traits.WIN32_FIND_DATA, flavor='raw') - try: - hFindFile = win32traits.FindFirstFile(l_path, filedata) - if hFindFile == rwin32.INVALID_HANDLE_VALUE: - return 0 - win32traits.FindClose(hFindFile) - data.c_dwFileAttributes = filedata.c_dwFileAttributes - rffi.structcopy(data.c_ftCreationTime, filedata.c_ftCreationTime) - rffi.structcopy(data.c_ftLastAccessTime, filedata.c_ftLastAccessTime) - rffi.structcopy(data.c_ftLastWriteTime, filedata.c_ftLastWriteTime) - data.c_nFileSizeHigh = filedata.c_nFileSizeHigh - data.c_nFileSizeLow = filedata.c_nFileSizeLow - return 1 - finally: - lltype.free(filedata, flavor='raw') - - def win32_stat_llimpl(path): - data = lltype.malloc(win32traits.WIN32_FILE_ATTRIBUTE_DATA, flavor='raw') - try: - l_path = traits.str2charp(path) - res = win32traits.GetFileAttributesEx(l_path, win32traits.GetFileExInfoStandard, data) - errcode = rwin32.GetLastError_saved() - if res == 0: - if errcode == win32traits.ERROR_SHARING_VIOLATION: - res = attributes_from_dir(l_path, data) - errcode = rwin32.GetLastError_saved() - traits.free_charp(l_path) - if res == 0: - raise WindowsError(errcode, "os_stat failed") - return attribute_data_to_stat(data) - finally: - lltype.free(data, flavor='raw') - - def win32_fstat_llimpl(fd): - handle = rwin32.get_osfhandle(fd) - filetype = win32traits.GetFileType(handle) - if filetype == win32traits.FILE_TYPE_CHAR: - # console or LPT device - return make_stat_result((win32traits._S_IFCHR, - 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)) - elif filetype == win32traits.FILE_TYPE_UNKNOWN: - error = rwin32.GetLastError_saved() - if error != 0: - raise WindowsError(error, "os_fstat failed") - # else: unknown but valid file - - # normal disk file (FILE_TYPE_DISK) - info = lltype.malloc(win32traits.BY_HANDLE_FILE_INFORMATION, - flavor='raw', zero=True) - try: - res = win32traits.GetFileInformationByHandle(handle, info) - if res == 0: - raise WindowsError(rwin32.GetLastError_saved(), - "os_fstat failed") - return by_handle_info_to_stat(info) - finally: - lltype.free(info, flavor='raw') - - if name == 'fstat': - return win32_fstat_llimpl - else: - return win32_stat_llimpl - - -#__________________________________________________ -# Helper functions for win32 - -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) - -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 time_t_to_FILE_TIME(time, filetime): - ft = rffi.r_longlong((time + secs_between_epochs) * 10000000) - filetime.c_dwHighDateTime = rffi.r_uint(ft >> 32) - filetime.c_dwLowDateTime = rffi.r_uint(ft) # masking off high bits diff --git a/rpython/rtyper/module/r_os_stat.py b/rpython/rtyper/module/r_os_stat.py deleted file mode 100644 --- a/rpython/rtyper/module/r_os_stat.py +++ /dev/null @@ -1,122 +0,0 @@ -""" -RTyping support for os.stat_result objects. -They are rtyped just like a tuple of the correct length supporting -only indexing and the st_xxx attributes. We need a custom StatResultRepr -because when rtyping for LL backends we have extra platform-dependent -items at the end of the tuple, but for OO backends we only want the -portable items. This allows the OO backends to assume a fixed shape for -the tuples returned by os.stat(). -""" -from rpython.annotator import model as annmodel -from rpython.rtyper.llannotation import lltype_to_annotation -from rpython.flowspace.model import Constant -from rpython.flowspace.operation import op -from rpython.tool.pairtype import pairtype -from rpython.rtyper.rmodel import Repr -from rpython.rtyper.rint import IntegerRepr -from rpython.rtyper.error import TyperError -from rpython.rtyper.module import ll_os_stat - - -class StatResultRepr(Repr): - - def __init__(self, rtyper): - self.rtyper = rtyper - self.stat_fields = ll_os_stat.STAT_FIELDS - - self.stat_field_indexes = {} - for i, (name, TYPE) in enumerate(self.stat_fields): - self.stat_field_indexes[name] = i - - self.s_tuple = annmodel.SomeTuple([lltype_to_annotation(TYPE) - for name, TYPE in self.stat_fields]) - self.r_tuple = rtyper.getrepr(self.s_tuple) - self.lowleveltype = self.r_tuple.lowleveltype - - def redispatch_getfield(self, hop, index): - rtyper = self.rtyper - s_index = rtyper.annotator.bookkeeper.immutablevalue(index) - hop2 = hop.copy() - spaceop = op.getitem(hop.args_v[0], Constant(index)) - spaceop.result = hop.spaceop.result - hop2.spaceop = spaceop - hop2.args_v = spaceop.args - hop2.args_s = [self.s_tuple, s_index] - hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)] - return hop2.dispatch() - - def rtype_getattr(self, hop): - s_attr = hop.args_s[1] - attr = s_attr.const - try: - index = self.stat_field_indexes[attr] - except KeyError: - raise TyperError("os.stat().%s: field not available" % (attr,)) - return self.redispatch_getfield(hop, index) - - -class __extend__(pairtype(StatResultRepr, IntegerRepr)): - - def rtype_getitem((r_sta, r_int), hop): - s_int = hop.args_s[1] - index = s_int.const - return r_sta.redispatch_getfield(hop, index) - - -def specialize_make_stat_result(hop): - r_StatResult = hop.rtyper.getrepr(ll_os_stat.s_StatResult) - [v_result] = hop.inputargs(r_StatResult.r_tuple) - # no-op conversion from r_StatResult.r_tuple to r_StatResult - hop.exception_cannot_occur() - return v_result - - -class StatvfsResultRepr(Repr): - - def __init__(self, rtyper): - self.rtyper = rtyper - self.statvfs_fields = ll_os_stat.STATVFS_FIELDS - - self.statvfs_field_indexes = {} - for i, (name, TYPE) in enumerate(self.statvfs_fields): - self.statvfs_field_indexes[name] = i - - self.s_tuple = annmodel.SomeTuple([lltype_to_annotation(TYPE) - for name, TYPE in self.statvfs_fields]) - self.r_tuple = rtyper.getrepr(self.s_tuple) - self.lowleveltype = self.r_tuple.lowleveltype - - def redispatch_getfield(self, hop, index): - rtyper = self.rtyper - s_index = rtyper.annotator.bookkeeper.immutablevalue(index) - hop2 = hop.copy() - spaceop = op.getitem(hop.args_v[0], Constant(index)) - spaceop.result = hop.spaceop.result - hop2.spaceop = spaceop - hop2.args_v = spaceop.args - hop2.args_s = [self.s_tuple, s_index] - hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)] - return hop2.dispatch() - - def rtype_getattr(self, hop): - s_attr = hop.args_s[1] - attr = s_attr.const - try: - index = self.statvfs_field_indexes[attr] - except KeyError: - raise TyperError("os.statvfs().%s: field not available" % (attr,)) - return self.redispatch_getfield(hop, index) - - -class __extend__(pairtype(StatvfsResultRepr, IntegerRepr)): - def rtype_getitem((r_sta, r_int), hop): - s_int = hop.args_s[1] - index = s_int.const - return r_sta.redispatch_getfield(hop, index) - - -def specialize_make_statvfs_result(hop): - r_StatvfsResult = hop.rtyper.getrepr(ll_os_stat.s_StatvfsResult) - [v_result] = hop.inputargs(r_StatvfsResult.r_tuple) - hop.exception_cannot_occur() - return v_result _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit