Author: Alex Gaynor <alex.gay...@gmail.com> Branch: add-statvfs Changeset: r65565:b0c0ac749b6b Date: 2013-07-23 09:59 -0700 http://bitbucket.org/pypy/pypy/changeset/b0c0ac749b6b/
Log: PyPy level statvfs and fstatvfs seem to work diff --git a/pypy/module/posix/__init__.py b/pypy/module/posix/__init__.py --- a/pypy/module/posix/__init__.py +++ b/pypy/module/posix/__init__.py @@ -17,12 +17,13 @@ 'setregid', 'setreuid', 'setsid', 'setuid', 'stat_float_times', 'statvfs', 'statvfs_result', 'symlink', 'sysconf', 'sysconf_names', 'tcgetpgrp', 'tcsetpgrp', 'ttyname', 'uname', 'wait', 'wait3', 'wait4' - ] +] # the Win32 urandom implementation isn't going to translate on JVM or CLI so # we have to remove it lltype_only_defs.append('urandom') + class Module(MixedModule): """This module provides access to operating system functionality that is standardized by the C Standard and the POSIX standard (a thinly @@ -32,21 +33,21 @@ applevel_name = os.name appleveldefs = { - 'error' : 'app_posix.error', - 'stat_result': 'app_posix.stat_result', - 'statvfs_result': 'app_posix.statvfs_result', - 'fdopen' : 'app_posix.fdopen', - 'tmpfile' : 'app_posix.tmpfile', - 'popen' : 'app_posix.popen', - 'tmpnam' : 'app_posix.tmpnam', - 'tempnam' : 'app_posix.tempnam', + 'error': 'app_posix.error', + 'stat_result': 'app_posix.stat_result', + 'statvfs_result': 'app_posix.statvfs_result', + 'fdopen': 'app_posix.fdopen', + 'tmpfile': 'app_posix.tmpfile', + 'popen': 'app_posix.popen', + 'tmpnam': 'app_posix.tmpnam', + 'tempnam': 'app_posix.tempnam', } if os.name == 'nt': appleveldefs.update({ - 'popen2' : 'app_posix.popen2', - 'popen3' : 'app_posix.popen3', - 'popen4' : 'app_posix.popen4', - }) + 'popen2': 'app_posix.popen2', + 'popen3': 'app_posix.popen3', + 'popen4': 'app_posix.popen4', + }) if hasattr(os, 'wait'): appleveldefs['wait'] = 'app_posix.wait' @@ -54,44 +55,49 @@ appleveldefs['wait3'] = 'app_posix.wait3' if hasattr(os, 'wait4'): appleveldefs['wait4'] = 'app_posix.wait4' - + interpleveldefs = { - 'open' : 'interp_posix.open', - 'lseek' : 'interp_posix.lseek', - 'write' : 'interp_posix.write', - 'isatty' : 'interp_posix.isatty', - 'read' : 'interp_posix.read', - 'close' : 'interp_posix.close', - 'closerange': 'interp_posix.closerange', - 'fstat' : 'interp_posix.fstat', - 'stat' : 'interp_posix.stat', - 'lstat' : 'interp_posix.lstat', - 'stat_float_times' : 'interp_posix.stat_float_times', - 'dup' : 'interp_posix.dup', - 'dup2' : 'interp_posix.dup2', - 'access' : 'interp_posix.access', - 'times' : 'interp_posix.times', - 'system' : 'interp_posix.system', - 'unlink' : 'interp_posix.unlink', - 'remove' : 'interp_posix.remove', - 'getcwd' : 'interp_posix.getcwd', - 'getcwdu' : 'interp_posix.getcwdu', - 'chdir' : 'interp_posix.chdir', - 'mkdir' : 'interp_posix.mkdir', - 'rmdir' : 'interp_posix.rmdir', - 'environ' : 'interp_posix.get(space).w_environ', - 'listdir' : 'interp_posix.listdir', - 'strerror' : 'interp_posix.strerror', - 'pipe' : 'interp_posix.pipe', - 'chmod' : 'interp_posix.chmod', - 'rename' : 'interp_posix.rename', - 'umask' : 'interp_posix.umask', - '_exit' : 'interp_posix._exit', - 'utime' : 'interp_posix.utime', - '_statfields': 'interp_posix.getstatfields(space)', - 'kill' : 'interp_posix.kill', - 'abort' : 'interp_posix.abort', - 'urandom' : 'interp_posix.urandom', + 'open': 'interp_posix.open', + 'lseek': 'interp_posix.lseek', + 'write': 'interp_posix.write', + 'isatty': 'interp_posix.isatty', + 'read': 'interp_posix.read', + 'close': 'interp_posix.close', + 'closerange': 'interp_posix.closerange', + + 'fstat': 'interp_posix.fstat', + 'stat': 'interp_posix.stat', + 'lstat': 'interp_posix.lstat', + 'stat_float_times': 'interp_posix.stat_float_times', + + 'fstatvfs': 'interp_posix.fstatvfs', + 'statvfs': 'interp_posix.statvfs', + + 'dup': 'interp_posix.dup', + 'dup2': 'interp_posix.dup2', + 'access': 'interp_posix.access', + 'times': 'interp_posix.times', + 'system': 'interp_posix.system', + 'unlink': 'interp_posix.unlink', + 'remove': 'interp_posix.remove', + 'getcwd': 'interp_posix.getcwd', + 'getcwdu': 'interp_posix.getcwdu', + 'chdir': 'interp_posix.chdir', + 'mkdir': 'interp_posix.mkdir', + 'rmdir': 'interp_posix.rmdir', + 'environ': 'interp_posix.get(space).w_environ', + 'listdir': 'interp_posix.listdir', + 'strerror': 'interp_posix.strerror', + 'pipe': 'interp_posix.pipe', + 'chmod': 'interp_posix.chmod', + 'rename': 'interp_posix.rename', + 'umask': 'interp_posix.umask', + '_exit': 'interp_posix._exit', + 'utime': 'interp_posix.utime', + '_statfields': 'interp_posix.getstatfields(space)', + 'kill': 'interp_posix.kill', + 'abort': 'interp_posix.abort', + 'urandom': 'interp_posix.urandom', } if hasattr(os, 'chown'): @@ -168,8 +174,8 @@ interpleveldefs['getlogin'] = 'interp_posix.getlogin' for name in ['setsid', 'getuid', 'geteuid', 'getgid', 'getegid', 'setuid', - 'seteuid', 'setgid', 'setegid', 'getgroups', 'getpgrp', - 'setpgrp', 'getppid', 'getpgid', 'setpgid', 'setreuid', + 'seteuid', 'setgid', 'setegid', 'getgroups', 'getpgrp', + 'setpgrp', 'getppid', 'getpgid', 'setpgid', 'setreuid', 'setregid', 'getsid', 'setsid']: if hasattr(os, name): interpleveldefs[name] = 'interp_posix.%s' % (name,) @@ -178,7 +184,7 @@ interpleveldefs['_getfullpathname'] = 'interp_posix._getfullpathname' if hasattr(os, 'chroot'): interpleveldefs['chroot'] = 'interp_posix.chroot' - + for name in RegisterOs.w_star: if hasattr(os, name): interpleveldefs[name] = 'interp_posix.' + name @@ -187,7 +193,7 @@ # if it's an ootype translation, remove all the defs that are lltype # only backend = space.config.translation.backend - if backend == 'cli' or backend == 'jvm': + if backend == 'cli' or backend == 'jvm' : for name in lltype_only_defs: self.interpleveldefs.pop(name, None) MixedModule.__init__(self, space, w_name) @@ -195,7 +201,7 @@ def startup(self, space): from pypy.module.posix import interp_posix interp_posix.get(space).startup(space) - + for constant in dir(os): value = getattr(os, constant) if constant.isupper() and type(value) is int: 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,15 +1,17 @@ -from pypy.interpreter.gateway import unwrap_spec +import os +import sys + from rpython.rlib import rposix, 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 rpython.rtyper.module.ll_os import RegisterOs + +from pypy.interpreter.gateway import unwrap_spec from pypy.interpreter.error import OperationError, wrap_oserror, wrap_oserror2 -from rpython.rtyper.module.ll_os import RegisterOs -from rpython.rtyper.module import ll_os_stat from pypy.module.sys.interp_encoding import getfilesystemencoding -import os -import sys _WIN32 = sys.platform == 'win32' if _WIN32: @@ -213,6 +215,7 @@ STAT_FIELDS = unrolling_iterable(enumerate(ll_os_stat.STAT_FIELDS)) PORTABLE_STAT_FIELDS = unrolling_iterable( enumerate(ll_os_stat.PORTABLE_STAT_FIELDS)) +STATVFS_FIELDS = unrolling_iterable(enumerate(ll_os_stat.STATVFS_FIELDS)) def build_stat_result(space, st): if space.config.translation.type_system == 'ootype': @@ -253,6 +256,16 @@ space.wrap('stat_result')) return space.call_function(w_stat_result, w_tuple, w_keywords) + +def build_statvfs_result(space, st): + vals_w = [None] * len(ll_os_stat.STATVFS_FIELDS) + for i, (name, _) in STATVFS_FIELDS: + vals_w[i] = space.wrap(getattr(st, name)) + w_tuple = space.newtuple(vals_w) + w_statvfs_result = space.getattr(space.getbuiltinmodule(os.name), space.wrap('statvfs_result')) + return space.call_function(w_statvfs_result, w_tuple) + + @unwrap_spec(fd=c_int) def fstat(space, fd): """Perform a stat system call on the file referenced to by an open @@ -314,6 +327,26 @@ else: state.stat_float_times = space.bool_w(w_value) + +@unwrap_spec(fd=c_int) +def fstatvfs(space, fd): + try: + st = os.fstat(fd) + except OSError as e: + raise wrap_oserror(space, e) + else: + return build_statvfs_result(space, st) + + +def statvfs(space, w_path): + try: + st = dispatch_filename(rposix.statvfs)(space, w_path) + except OSError as e: + raise wrap_oserror2(space, e, w_path) + else: + return build_statvfs_result(space, st) + + @unwrap_spec(fd=c_int) def dup(space, fd): """Create a copy of the file descriptor. Return the new file 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 @@ -169,7 +169,8 @@ assert stat.S_ISDIR(st.st_mode) def test_stat_exception(self): - import sys, errno + import sys + import errno for fn in [self.posix.stat, self.posix.lstat]: try: fn("nonexistentdir/nonexistentfile") @@ -183,6 +184,15 @@ assert isinstance(e, WindowsError) assert e.winerror == 3 + def test_statvfs(self): + st = self.posix.statvfs(".") + assert isinstance(st, self.posix.statvfs_result) + for field in [ + 'f_bsize', 'f_frsize', 'f_blocks', 'f_bfree', 'f_bavail', + 'f_files', 'f_ffree', 'f_favail', 'f_flag', 'f_namemax', + ]: + assert hasattr(st, field) + def test_pickle(self): import pickle, os st = self.posix.stat(os.curdir) diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -154,6 +154,15 @@ else: return os.lstat(path.as_bytes()) + +@specialize.argtype(0) +def statvfs(path): + if isinstance(path, str): + return os.statvfs(path) + else: + return os.statvfs(path.as_bytes()) + + @specialize.argtype(0) def unlink(path): if isinstance(path, str): 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 @@ -1698,6 +1698,12 @@ from rpython.rtyper.module import ll_os_stat return ll_os_stat.register_stat_variant('lstat', traits) + @registering(os.fstatvfs) + def register_os_fstatvfs(self): + from rpython.rtyper.module import ll_os_stat + return ll_os_stat.register_statvfs_variant('fstatvfs', StringTraits()) + + # ------------------------------- os.W* --------------------------------- w_star = ['WCOREDUMP', 'WIFCONTINUED', 'WIFSTOPPED', diff --git a/rpython/rtyper/module/ll_os_stat.py b/rpython/rtyper/module/ll_os_stat.py --- a/rpython/rtyper/module/ll_os_stat.py +++ b/rpython/rtyper/module/ll_os_stat.py @@ -2,20 +2,22 @@ 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 + +import os +import sys + from rpython.annotator import model as annmodel -from rpython.tool.pairtype import pairtype -from rpython.tool.sourcetools import func_with_new_name, func_renamer -from rpython.rtyper import extregistry -from rpython.rtyper.extfunc import register_external, extdef -from rpython.rtyper.lltypesystem import rffi, lltype -from rpython.rtyper.tool import rffi_platform as platform -from rpython.rtyper.lltypesystem.rtupletype import TUPLE_TYPE from rpython.rlib import rposix from rpython.rlib.rarithmetic import intmask -from rpython.rlib.objectmodel import specialize +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.lltypesystem.rtupletype 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 -from rpython.rtyper.annlowlevel import hlstr # Support for float times is here. # - ALL_STAT_FIELDS contains Float fields if the system can retrieve @@ -47,12 +49,26 @@ ("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 @@ -79,6 +95,7 @@ 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([annmodel.lltype_to_annotation(TYPE) @@ -86,6 +103,23 @@ extra_zeroes = (0,) * (len(STAT_FIELDS) - len(PORTABLE_STAT_FIELDS)) return s_reduced, stat_result_reduce, stat_result_recreate + +class SomeStatvfsResult(annmodel.SomeObject): + knowntype = os.statvfs_result + + def rtyper_makerepr(self, rtyper): + from rpython.rtyper.module import r_os_stat + return r_os_stat.StatvfsResultRepr(rtyper) + + def rtyper_makekey_ex(self, rtyper): + return self.__class__, + + def getattr(self, s_attr): + assert s_attr.is_constant() + TYPE = STATVFS_FIELD_TYPES[s_attr.const] + return annmodel.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" @@ -94,8 +128,17 @@ name, TYPE = STAT_FIELDS[index] return annmodel.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 annmodel.lltype_to_annotation(TYPE) + + s_StatResult = SomeStatResult() + def make_stat_result(tup): """Turn a tuple into an os.stat_result object.""" positional = tup[:N_INDEXABLE_FIELDS] @@ -104,6 +147,11 @@ 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 @@ -120,16 +168,16 @@ if sys.platform.startswith('win'): _name_struct_stat = '_stati64' - INCLUDES = ['sys/types.h', 'sys/stat.h'] + INCLUDES = ['sys/types.h', 'sys/stat.h', 'sys/statvfs.h'] else: _name_struct_stat = 'stat' - INCLUDES = ['sys/types.h', 'sys/stat.h', 'unistd.h'] + 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 + includes=INCLUDES ) if TIMESPEC is not None: @@ -141,7 +189,7 @@ def posix_declaration(try_to_add=None): - global STAT_STRUCT + global STAT_STRUCT, STATVFS_STRUCT LL_STAT_FIELDS = STAT_FIELDS[:] if try_to_add: @@ -173,15 +221,17 @@ 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) + 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) @@ -202,6 +252,9 @@ 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 @@ -233,6 +286,21 @@ 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 @@ -301,6 +369,61 @@ [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 + ) + + @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_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) + 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_statvfs_llimpl, llfakeimpl=posix_fakeimpl + ) + + def make_win32_stat_impl(name, traits): from rpython.rlib import rwin32 from rpython.rtyper.module.ll_win32file import make_win32_traits _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit