Author: Maciej Fijalkowski <fij...@gmail.com> Branch: fileops2 Changeset: r67163:7c6969e0bd84 Date: 2013-10-06 10:51 +0200 http://bitbucket.org/pypy/pypy/changeset/7c6969e0bd84/
Log: (fijal, arigo) work on fileops diff --git a/rpython/flowspace/specialcase.py b/rpython/flowspace/specialcase.py --- a/rpython/flowspace/specialcase.py +++ b/rpython/flowspace/specialcase.py @@ -1,3 +1,4 @@ +import os from rpython.flowspace.model import Constant, const SPECIAL_CASES = {} @@ -37,6 +38,18 @@ return space.frame.do_operation('simple_call', const(isinstance), w_instance, w_type) +@register_flow_sc(open) +def sc_open(space, *args_w): + from rpython.rlib.rfile import create_file + + return space.frame.do_operation("simple_call", const(create_file), *args_w) + +@register_flow_sc(os.tmpfile) +def sc_os_tmpfile(space): + from rpython.rlib.rfile import create_temp_rfile + + return space.frame.do_operation("simple_call", const(create_temp_rfile)) + # _________________________________________________________________________ # a simplified version of the basic printing routines, for RPython programs class StdOutBuffer: diff --git a/rpython/rlib/rfile.py b/rpython/rlib/rfile.py --- a/rpython/rlib/rfile.py +++ b/rpython/rlib/rfile.py @@ -1,55 +1,158 @@ -""" This file makes open() and friends RPython +""" This file makes open() and friends RPython. Note that RFile should not +be used directly and instead it's magically appearing each time you call +python builtin open() """ import os -from rpython.annotator.model import SomeObject, SomeString, SomeInteger -from rpython.rtyper.extregistry import ExtRegistryEntry -from rpython.rtyper.extfunc import register_external +from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.translator.tool.cbuild import ExternalCompilationInfo +from rpython.rlib.rarithmetic import r_uint, intmask +from rpython.rlib import rposix +from rpython.rlib.rstring import StringBuilder -class SomeFile(SomeObject): - def method_write(self, s_arg): - assert isinstance(s_arg, SomeString) +eci = ExternalCompilationInfo(includes=['stdio.h']) - def method_read(self, s_arg=None): - if s_arg is not None: - assert isinstance(s_arg, SomeInteger) - return SomeString(can_be_None=False) +def llexternal(*args): + return rffi.llexternal(*args, compilation_info=eci) - def method_close(self): - pass +FILE = lltype.Struct('FILE') # opaque type maybe - def method_seek(self, s_arg, s_whence=None): - assert isinstance(s_arg, SomeInteger) - if s_whence is not None: - assert isinstance(s_whence, SomeInteger) +c_open = llexternal('fopen', [rffi.CCHARP, rffi.CCHARP], lltype.Ptr(FILE)) +c_close = llexternal('fclose', [lltype.Ptr(FILE)], rffi.INT) +c_write = llexternal('fwrite', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, + lltype.Ptr(FILE)], rffi.SIZE_T) +c_read = llexternal('fread', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, + lltype.Ptr(FILE)], rffi.SIZE_T) +c_feof = llexternal('feof', [lltype.Ptr(FILE)], rffi.INT) +c_ferror = llexternal('ferror', [lltype.Ptr(FILE)], rffi.INT) +c_clearerror = llexternal('clearerr', [lltype.Ptr(FILE)], lltype.Void) +c_fseek = llexternal('fseek', [lltype.Ptr(FILE), rffi.LONG, rffi.INT], + rffi.INT) +c_tmpfile = llexternal('tmpfile', [], lltype.Ptr(FILE)) +c_fileno = llexternal('fileno', [lltype.Ptr(FILE)], rffi.INT) +c_ftell = llexternal('ftell', [lltype.Ptr(FILE)], lltype.Signed) +c_fflush = llexternal('fflush', [lltype.Ptr(FILE)], lltype.Signed) - def rtyper_makekey(self): - return self.__class__, +BASE_BUF_SIZE = 4096 - def rtyper_makerepr(self, rtyper): - from rpython.rtyper.lltypesystem.rfile import FileRepr +def create_file(filename, mode="r", buffering=-1): + assert buffering == -1 + assert filename is not None + assert mode is not None + ll_name = rffi.str2charp(filename) + try: + ll_mode = rffi.str2charp(mode) + try: + ll_f = c_open(ll_name, ll_mode) + if not ll_f: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + finally: + lltype.free(ll_mode, flavor='raw') + finally: + lltype.free(ll_name, flavor='raw') + return RFile(ll_f) - return FileRepr(rtyper) +def create_temp_rfile(): + res = c_tmpfile() + if not res: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + return RFile(res) -class FileEntry(ExtRegistryEntry): - _about_ = open +class RFile(object): + def __init__(self, ll_file): + self.ll_file = ll_file - def compute_result_annotation(self, s_name, s_mode=None): - assert isinstance(s_name, SomeString) - if s_mode is not None: - assert isinstance(s_mode, SomeString) - return SomeFile() + def write(self, value): + assert value is not None + ll_file = self.ll_file + if not ll_file: + raise ValueError("I/O operation on closed file") + assert value is not None + ll_value = rffi.get_nonmovingbuffer(value) + try: + # note that since we got a nonmoving buffer, it is either raw + # or already cannot move, so the arithmetics below are fine + total_bytes = 0 + ll_current = ll_value + while total_bytes < len(value): + bytes = c_write(ll_current, 1, len(value) - r_uint(total_bytes), + ll_file) + if bytes == 0: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + total_bytes += bytes + ll_current = rffi.cast(rffi.CCHARP, + rffi.cast(lltype.Unsigned, ll_value) + + total_bytes) + finally: + rffi.free_nonmovingbuffer(value, ll_value) - def specialize_call(self, hop): - return hop.r_result.rtype_constructor(hop) + def close(self): + if self.ll_file: + # double close is allowed + res = c_close(self.ll_file) + self.ll_file = lltype.nullptr(FILE) + if res == -1: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) -class OSTempfileEntry(ExtRegistryEntry): - _about_ = os.tmpfile + def read(self, size=-1): + ll_file = self.ll_file + if not ll_file: + raise ValueError("I/O operation on closed file") + if size < 0: + # read the entire contents + buf = lltype.malloc(rffi.CCHARP.TO, BASE_BUF_SIZE, flavor='raw') + try: + s = StringBuilder() + while True: + returned_size = c_read(buf, 1, BASE_BUF_SIZE, ll_file) + if returned_size == 0: + if c_feof(ll_file): + # ok, finished + return s.build() + errno = c_ferror(ll_file) + c_clearerror(ll_file) + raise OSError(errno, os.strerror(errno)) + s.append_charpsize(buf, returned_size) + finally: + lltype.free(buf, flavor='raw') + else: + raw_buf, gc_buf = rffi.alloc_buffer(size) + try: + returned_size = c_read(raw_buf, 1, size, ll_file) + if returned_size == 0: + if not c_feof(ll_file): + errno = c_ferror(ll_file) + raise OSError(errno, os.strerror(errno)) + s = rffi.str_from_buffer(raw_buf, gc_buf, size, + rffi.cast(lltype.Signed, returned_size)) + finally: + rffi.keep_buffer_alive_until_here(raw_buf, gc_buf) + return s - def compute_result_annotation(self): - return SomeFile() + def seek(self, pos, whence=0): + ll_file = self.ll_file + if not ll_file: + raise ValueError("I/O operation on closed file") + res = c_fseek(ll_file, pos, whence) + if res == -1: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) - def specialize_call(self, hop): - return hop.r_result.rtype_tempfile(hop) + def fileno(self): + if self.ll_file: + return intmask(c_fileno(self.ll_file)) + raise ValueError("I/O operation on closed file") + def tell(self): + if self.ll_file: + res = intmask(c_ftell(self.ll_file)) + if res == -1: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + return res + raise ValueError("I/O operation on closed file") diff --git a/rpython/rlib/test/test_rfile.py b/rpython/rlib/test/test_rfile.py --- a/rpython/rlib/test/test_rfile.py +++ b/rpython/rlib/test/test_rfile.py @@ -78,3 +78,31 @@ f() self.interpret(f, []) + + def test_fileno(self): + fname = str(self.tmpdir.join('file_5')) + + def f(): + f = open(fname, "w") + try: + return f.fileno() + finally: + f.close() + + res = self.interpret(f, []) + assert res > 2 + + def test_tell(self): + fname = str(self.tmpdir.join('file_tell')) + + def f(): + f = open(fname, "w") + f.write("xyz") + try: + return f.tell() + finally: + f.close() + + res = self.interpret(f, []) + assert res == 3 + diff --git a/rpython/rtyper/lltypesystem/rfile.py b/rpython/rtyper/lltypesystem/rfile.py deleted file mode 100644 --- a/rpython/rtyper/lltypesystem/rfile.py +++ /dev/null @@ -1,195 +0,0 @@ - -import os -from rpython.rlib import rposix -from rpython.rlib.rarithmetic import r_uint -from rpython.annotator import model as annmodel -from rpython.rtyper.rtyper import Repr -from rpython.rlib.rstring import StringBuilder -from rpython.rtyper.lltypesystem import lltype, rffi, llmemory -from rpython.rtyper.lltypesystem.rstr import string_repr, STR -from rpython.translator.tool.cbuild import ExternalCompilationInfo -from rpython.rtyper.annlowlevel import hlstr -from rpython.rtyper.lltypesystem.lloperation import llop - -FILE = lltype.Struct('FILE') # opaque type maybe -FILE_WRAPPER = lltype.GcStruct("FileWrapper", ('file', lltype.Ptr(FILE))) - -eci = ExternalCompilationInfo(includes=['stdio.h']) - -def llexternal(*args): - return rffi.llexternal(*args, compilation_info=eci) - -c_open = llexternal('fopen', [rffi.CCHARP, rffi.CCHARP], lltype.Ptr(FILE)) -c_close = llexternal('fclose', [lltype.Ptr(FILE)], rffi.INT) -c_write = llexternal('fwrite', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, - lltype.Ptr(FILE)], rffi.SIZE_T) -c_read = llexternal('fread', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, - lltype.Ptr(FILE)], rffi.SIZE_T) -c_feof = llexternal('feof', [lltype.Ptr(FILE)], rffi.INT) -c_ferror = llexternal('ferror', [lltype.Ptr(FILE)], rffi.INT) -c_clearerror = llexternal('clearerr', [lltype.Ptr(FILE)], lltype.Void) -c_fseek = llexternal('fseek', [lltype.Ptr(FILE), rffi.LONG, rffi.INT], - rffi.INT) -c_tmpfile = llexternal('tmpfile', [], lltype.Ptr(FILE)) - -def ll_open(name, mode): - file_wrapper = lltype.malloc(FILE_WRAPPER) - ll_name = rffi.str2charp(name) - ll_mode = rffi.str2charp(mode) - try: - ll_f = c_open(ll_name, ll_mode) - if not ll_f: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - file_wrapper.file = ll_f - finally: - lltype.free(ll_name, flavor='raw') - lltype.free(ll_mode, flavor='raw') - return file_wrapper - -def ll_tmpfile(): - file_wrapper = lltype.malloc(FILE_WRAPPER) - res = c_tmpfile() - if not res: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - file_wrapper.file = res - return file_wrapper - -def ll_write(file_wrapper, value): - ll_file = file_wrapper.file - if not ll_file: - raise ValueError("I/O operation on closed file") - value = hlstr(value) - assert value is not None - ll_value = rffi.get_nonmovingbuffer(value) - try: - # note that since we got a nonmoving buffer, it is either raw - # or already cannot move, so the arithmetics below are fine - total_bytes = 0 - ll_current = ll_value - while total_bytes < len(value): - bytes = c_write(ll_current, 1, len(value) - r_uint(total_bytes), - ll_file) - if bytes == 0: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - total_bytes += bytes - ll_current = rffi.cast(rffi.CCHARP, - rffi.cast(lltype.Unsigned, ll_value) + - total_bytes) - finally: - rffi.free_nonmovingbuffer(value, ll_value) - -BASE_BUF_SIZE = 4096 - -def ll_read(file_wrapper, size): - ll_file = file_wrapper.file - if not ll_file: - raise ValueError("I/O operation on closed file") - if size < 0: - # read the entire contents - buf = lltype.malloc(rffi.CCHARP.TO, BASE_BUF_SIZE, flavor='raw') - try: - s = StringBuilder() - while True: - returned_size = c_read(buf, 1, BASE_BUF_SIZE, ll_file) - if returned_size == 0: - if c_feof(ll_file): - # ok, finished - return s.build() - errno = c_ferror(ll_file) - c_clearerror(ll_file) - raise OSError(errno, os.strerror(errno)) - s.append_charpsize(buf, returned_size) - finally: - lltype.free(buf, flavor='raw') - else: - raw_buf, gc_buf = rffi.alloc_buffer(size) - try: - returned_size = c_read(raw_buf, 1, size, ll_file) - if returned_size == 0: - if not c_feof(ll_file): - errno = c_ferror(ll_file) - raise OSError(errno, os.strerror(errno)) - s = rffi.str_from_buffer(raw_buf, gc_buf, size, - rffi.cast(lltype.Signed, returned_size)) - finally: - rffi.keep_buffer_alive_until_here(raw_buf, gc_buf) - return s -def ll_seek(file_wrapper, pos, whence): - ll_file = file_wrapper.file - if not ll_file: - raise ValueError("I/O operation on closed file") - res = c_fseek(ll_file, pos, whence) - if res == -1: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - -def ll_close(file_wrapper): - if file_wrapper.file: - # double close is allowed - res = c_close(file_wrapper.file) - file_wrapper.file = lltype.nullptr(FILE) - if res == -1: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - -class FileRepr(Repr): - lowleveltype = lltype.Ptr(FILE_WRAPPER) - - def __init__(self, typer): - Repr.__init__(self) - - def rtype_constructor(self, hop): - repr = hop.rtyper.getrepr(annmodel.SomeString()) - arg_0 = hop.inputarg(repr, 0) - if len(hop.args_v) == 1: - arg_1 = hop.inputconst(string_repr, "r") - else: - arg_1 = hop.inputarg(repr, 1) - hop.exception_is_here() - open = hop.rtyper.getannmixlevel().delayedfunction( - ll_open, [annmodel.SomeString()] * 2, - annmodel.SomePtr(self.lowleveltype)) - v_open = hop.inputconst(lltype.typeOf(open), open) - return hop.genop('direct_call', [v_open, arg_0, arg_1], - resulttype=self) - - def rtype_tempfile(self, hop): - tmpfile = hop.rtyper.getannmixlevel().delayedfunction( - ll_tmpfile, [], annmodel.SomePtr(self.lowleveltype)) - v_tmpfile = hop.inputconst(lltype.typeOf(tmpfile), tmpfile) - hop.exception_is_here() - return hop.genop('direct_call', [v_tmpfile], resulttype=self) - - - def rtype_method_write(self, hop): - args_v = hop.inputargs(self, string_repr) - hop.exception_is_here() - return hop.gendirectcall(ll_write, *args_v) - - def rtype_method_close(self, hop): - r_self = hop.inputarg(self, 0) - hop.exception_is_here() - return hop.gendirectcall(ll_close, r_self) - - def rtype_method_read(self, hop): - r_self = hop.inputarg(self, 0) - if len(hop.args_v) != 2: - arg_1 = hop.inputconst(lltype.Signed, -1) - else: - arg_1 = hop.inputarg(lltype.Signed, 1) - hop.exception_is_here() - return hop.gendirectcall(ll_read, r_self, arg_1) - - def rtype_method_seek(self, hop): - r_self = hop.inputarg(self, 0) - arg_1 = hop.inputarg(lltype.Signed, 1) - if len(hop.args_v) != 3: - arg_2 = hop.inputconst(lltype.Signed, os.SEEK_SET) - else: - arg_2 = hop.inputarg(lltype.Signed, 2) - hop.exception_is_here() - return hop.gendirectcall(ll_seek, r_self, arg_1, arg_2) - _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit