[pypy-svn] r34185 - in pypy/branch/refactor-file/pypy: lib lib/test2 module/_file module/_file/test rlib

cfbolz at codespeak.net cfbolz at codespeak.net
Sat Nov 4 16:35:44 CET 2006


Author: cfbolz
Date: Sat Nov  4 16:35:43 2006
New Revision: 34185

Added:
   pypy/branch/refactor-file/pypy/module/_file/   (props changed)
   pypy/branch/refactor-file/pypy/module/_file/__init__.py   (contents, props changed)
   pypy/branch/refactor-file/pypy/module/_file/app_file.py   (contents, props changed)
   pypy/branch/refactor-file/pypy/module/_file/interp_file.py   (contents, props changed)
   pypy/branch/refactor-file/pypy/module/_file/test/   (props changed)
   pypy/branch/refactor-file/pypy/module/_file/test/__init__.py   (contents, props changed)
   pypy/branch/refactor-file/pypy/module/_file/test/test_file.py   (contents, props changed)
   pypy/branch/refactor-file/pypy/module/_file/test/test_file_extra.py
      - copied, changed from r34171, pypy/branch/refactor-file/pypy/lib/test2/test_file_extra.py
Removed:
   pypy/branch/refactor-file/pypy/lib/_file.py
   pypy/branch/refactor-file/pypy/lib/test2/test_file_extra.py
Modified:
   pypy/branch/refactor-file/pypy/rlib/streamio.py
Log:
make _file a mixed module. seems to work even!


Added: pypy/branch/refactor-file/pypy/module/_file/__init__.py
==============================================================================
--- (empty file)
+++ pypy/branch/refactor-file/pypy/module/_file/__init__.py	Sat Nov  4 16:35:43 2006
@@ -0,0 +1,14 @@
+
+# Package initialisation
+from pypy.interpreter.mixedmodule import MixedModule
+
+class Module(MixedModule):
+    appleveldefs = {
+        "file": "app_file.file",
+    }
+
+    interpleveldefs = {
+        "open_file_as_stream": "interp_file.open_file_as_stream",
+        "fdopen_as_stream": "interp_file.fdopen_as_stream",
+    }
+

Added: pypy/branch/refactor-file/pypy/module/_file/app_file.py
==============================================================================
--- (empty file)
+++ pypy/branch/refactor-file/pypy/module/_file/app_file.py	Sat Nov  4 16:35:43 2006
@@ -0,0 +1,252 @@
+"""NOT_RPYTHON"""
+
+import os
+
+class file(object):
+    """file(name[, mode[, buffering]]) -> file object
+
+Open a file.  The mode can be 'r', 'w' or 'a' for reading (default),
+writing or appending.  The file will be created if it doesn't exist
+when opened for writing or appending; it will be truncated when
+opened for writing.  Add a 'b' to the mode for binary files.
+Add a '+' to the mode to allow simultaneous reading and writing.
+If the buffering argument is given, 0 means unbuffered, 1 means line
+buffered, and larger numbers specify the buffer size.
+Add a 'U' to mode to open the file for input with universal newline
+support.  Any line ending in the input file will be seen as a '\n'
+in Python.  Also, a file so opened gains the attribute 'newlines';
+the value for this attribute is one of None (no newline read yet),
+'\r', '\n', '\r\n' or a tuple containing all the newline types seen.
+
+Note:  open() is an alias for file().
+    """
+
+    _closed = True   # Until the file is successfully opened
+
+    def __init__(self, name, mode='r', buffering=-1):
+        import _file
+        self._name = name
+        self.softspace = 0    # Required according to file object docs
+        self.encoding = None  # This is not used internally by file objects
+        self._closed = False
+        self.stream = _file.open_file_as_stream(self._name, mode, buffering)
+        self._mode = mode
+        self.fd = self.stream.try_to_find_file_descriptor()
+        assert self.fd != -1
+        
+    def fdopen(cls, fd, mode='r', buffering=-1):
+        f = cls.__new__(cls)
+        f._fdopen(fd, mode, buffering, '<fdopen>')
+        return f
+    fdopen = classmethod(fdopen)
+
+    def _fdopen(self, fd, mode, buffering, name):
+        import _file
+        self.fd = fd
+        self._name = name
+        self.softspace = 0    # Required according to file object docs
+        self.encoding = None  # This is not used internally by file objects
+        self._closed = False
+        self.stream = _file.fdopen_as_stream(fd, mode, buffering)
+        self._mode = mode
+        
+    def getnewlines(self):
+        "end-of-line convention used in this file"
+        if isinstance(self.stream, _sio.TextInputFilter):
+            return self.stream.getnewlines()
+        else:
+            return None
+
+    mode     = property(lambda self: self._mode,
+                        doc = "file mode ('r', 'U', 'w', 'a', "
+                              "possibly with 'b' or '+' added)")
+    name     = property(lambda self: self._name, doc = "file name")
+    closed   = property(lambda self: self._closed,
+                        doc = "True if the file is closed")
+    newlines = property(lambda self: self.getnewlines(),
+                        doc = "end-of-line convention used in this file")
+
+    def read(self, n=-1):
+        """read([size]) -> read at most size bytes, returned as a string.
+
+If the size argument is negative or omitted, read until EOF is reached.
+Notice that when in non-blocking mode, less data than what was requested
+may be returned, even if no size parameter was given."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        if n < 0:
+            return self.stream.readall()
+        else:
+            result = []
+            while n > 0:
+                data = self.stream.read(n)
+                if not data:
+                    break
+                n -= len(data)
+                result.append(data)
+            return ''.join(result)
+
+    def readline(self, size=-1):
+        """readline([size]) -> next line from the file, as a string.
+
+Retain newline.  A non-negative size argument limits the maximum
+number of bytes to return (an incomplete line may be returned then).
+Return an empty string at EOF."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        if size < 0:
+            return self.stream.readline()
+        else:
+            # XXX slow
+            chars = []
+            for i in xrange(size):
+                char = self.stream.read(1)
+                chars.append(char)
+                if char == '' or char == '\n':
+                    break
+            return ''.join(chars)
+
+    def readlines(self, size=-1):
+        """readlines([size]) -> list of strings, each a line from the file.
+
+Call readline() repeatedly and return a list of the lines so read.
+The optional size argument, if given, is an approximate bound on the
+total number of bytes in the lines returned."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        if size < 0:
+            return list(iter(self.stream.readline, ""))
+        else:
+            result = []
+            while size > 0:
+                line = self.stream.readline()
+                if not line:
+                    break
+                result.append(line)
+                size -= len(line)
+            return result
+
+    def write(self, data):
+        """write(str) -> None.  Write string str to file.
+
+Note that due to buffering, flush() or close() may be needed before
+the file on disk reflects the data written."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        if not isinstance(data, str):
+            raise TypeError('write() argument must be a string (for now)')
+        return self.stream.write(data)
+
+    def writelines(self, sequence_of_strings):
+        """writelines(sequence_of_strings) -> None.  Write the strings to the file.
+
+Note that newlines are not added.  The sequence can be any iterable object
+producing strings. This is equivalent to calling write() for each string."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        for line in sequence_of_strings:
+            if not isinstance(line, str):
+                raise TypeError('writelines() argument must be a list '
+                                'of strings')
+            self.stream.write(line)
+
+    def tell(self):
+        """tell() -> current file position, an integer (may be a long integer)."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        return self.stream.tell()
+    
+    def seek(self, offset, whence=0):
+        """seek(offset[, whence]) -> None.  Move to new file position.
+
+Argument offset is a byte count.  Optional argument whence defaults to
+0 (offset from start of file, offset should be >= 0); other values are 1
+(move relative to current position, positive or negative), and 2 (move
+relative to end of file, usually negative, although many platforms allow
+seeking beyond the end of a file).  If the file is opened in text mode,
+only offsets returned by tell() are legal.  Use of other offsets causes
+undefined behavior.
+Note that not all file objects are seekable."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        self.stream.seek(offset, whence)
+
+    def __iter__(self):
+        """Iterating over files, as in 'for line in f:', returns each line of
+the file one by one."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        return self
+    xreadlines = __iter__
+    
+    def next(self):
+        """next() -> the next line in the file, or raise StopIteration"""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        line = self.stream.readline()
+        if line == '':
+            raise StopIteration
+        return line
+
+    def truncate(self, size=None):
+        """truncate([size]) -> None.  Truncate the file to at most size bytes.
+
+Size defaults to the current file position, as returned by tell()."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        if size is None:
+            size = self.stream.tell()
+        self.stream.truncate(size)
+
+    def flush(self):
+        """flush() -> None.  Flush the internal I/O buffer."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        self.stream.flush()
+
+    def close(self):
+        """close() -> None or (perhaps) an integer.  Close the file.
+
+Sets data attribute .closed to True.  A closed file cannot be used for
+further I/O operations.  close() may be called more than once without
+error.  Some kinds of file objects (for example, opened by popen())
+may return an exit status upon closing."""
+        if not self._closed:
+            self._closed = True
+            self.stream.close()
+
+    __del__ = close
+
+    def readinto(self, a):
+        """readinto() -> Undocumented.  Don't use this; it may go away."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        from array import array
+        if not isinstance(a, array):
+            raise TypeError('Can only read into array objects')
+        length = len(a)
+        data = self.read(length)
+        del a[:]
+        a.fromstring(data + '\x00' * (length-len(data)))
+        return len(data)
+
+    def fileno(self):
+        '''fileno() -> integer "file descriptor".
+
+This is needed for lower-level file interfaces, such os.read().'''
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        return self.fd
+
+    def isatty(self):
+        """isatty() -> true or false.  True if the file is connected to a tty device."""
+        if self._closed:
+            raise ValueError('I/O operation on closed file')
+        return os.isatty(self.fd)
+
+    def __repr__(self):
+        return '<%s file %r, mode %r at 0x%x>' % (
+            self._closed and 'closed' or 'open',
+            self._name,
+            self._mode,
+            id(self))

Added: pypy/branch/refactor-file/pypy/module/_file/interp_file.py
==============================================================================
--- (empty file)
+++ pypy/branch/refactor-file/pypy/module/_file/interp_file.py	Sat Nov  4 16:35:43 2006
@@ -0,0 +1,83 @@
+import py
+from pypy.rlib import streamio
+
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import ObjSpace, W_Root, NoneNotWrapped, applevel
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app
+
+import os
+
+
+def wrap_oserror_as_ioerror(space, e):
+    assert isinstance(e, OSError)
+    errno = e.errno
+    try:
+        msg = os.strerror(errno)
+    except ValueError:
+        msg = 'error %d' % errno
+    w_error = space.call_function(space.w_IOError,
+                                  space.wrap(errno),
+                                  space.wrap(msg))
+    return OperationError(space.w_IOError, w_error)
+
+
+EXPOSED_STREAM_METHODS = [
+    ("read", [int]),
+    ("write", [str]),
+    ("tell", []),
+    ("seek", [int, int]),
+    ("readall", []),
+    ("readline", []),
+    ("truncate", [int]),
+    ("flush", []),
+    ("close", []),
+    ("peek", []),
+    ("try_to_find_file_descriptor", []),
+    ]
+
+class W_Stream(Wrappable):
+    def __init__(self, space, stream):
+        self.stream = stream
+
+for name, argtypes in EXPOSED_STREAM_METHODS:
+    numargs = len(argtypes)
+    args = ", ".join(["v%s" % i for i in range(numargs)])
+    exec py.code.Source("""
+    def %(name)s(self, space, %(args)s):
+        try:
+            return space.wrap(self.stream.%(name)s(%(args)s))
+        except streamio.StreamError, e:
+            raise OperationError(space.w_ValueError,
+                                 space.wrap(e.message))
+        except OSError, e:
+            raise wrap_oserror_as_ioerror(space, e)
+    %(name)s.unwrap_spec = [W_Stream, ObjSpace] + argtypes
+    """ % locals()).compile() in globals()
+
+W_Stream.typedef = TypeDef("Stream",
+    **dict([(name, interp2app(globals()[name]))
+                for name, _ in EXPOSED_STREAM_METHODS]))
+
+
+def is_mode_ok(space, mode):
+    if not mode or mode[0] not in ['r', 'w', 'a', 'U']:
+        raise OperationError(
+                IOError,
+                space.wrap('invalid mode : %s' % mode))
+
+def open_file_as_stream(space, path, mode="r", buffering=-1):
+    is_mode_ok(space, mode)
+    try:
+        return space.wrap(W_Stream(
+            space, streamio.open_file_as_stream(path, mode, buffering)))
+    except OSError, e:
+        raise wrap_oserror_as_ioerror(e)
+open_file_as_stream.unwrap_spec = [ObjSpace, str, str, int]
+
+def fdopen_as_stream(space, fd, mode="r", buffering=-1):
+    is_mode_ok(space, mode)
+    return space.wrap(W_Stream(
+            space, streamio.fdopen_as_stream(fd, mode, buffering)))
+fdopen_as_stream.unwrap_spec = [ObjSpace, int, str, int]

Added: pypy/branch/refactor-file/pypy/module/_file/test/__init__.py
==============================================================================

Added: pypy/branch/refactor-file/pypy/module/_file/test/test_file.py
==============================================================================
--- (empty file)
+++ pypy/branch/refactor-file/pypy/module/_file/test/test_file.py	Sat Nov  4 16:35:43 2006
@@ -0,0 +1,43 @@
+import py
+
+from pypy.conftest import gettestobjspace
+
+class AppTestFile(object):
+    def setup_class(cls):
+        cls.space = gettestobjspace(usemodules=("_file", ))
+        cls.w_temppath = cls.space.wrap(
+            str(py.test.ensuretemp("fileimpl").join("foo.txt")))
+
+    def test_simple(self):
+        import _file
+        f = _file.file(self.temppath, "w")
+        try:
+            f.write("foo")
+        finally:
+            f.close()
+        f = _file.file(self.temppath, "r")
+        try:
+            s = f.read()
+            assert s == "foo"
+        finally:
+            f.close()
+
+    def test_fdopen(self):
+        import _file, os
+        f = _file.file(self.temppath, "w")
+        try:
+            f.write("foo")
+        finally:
+            f.close()
+        fd = os.open(self.temppath, os.O_WRONLY | os.O_CREAT)
+        f2 = _file.file.fdopen(fd, "a")
+        f2.seek(0, 2)
+        f2.write("bar")
+        f2.close()
+        # don't close fd, will get a whining __del__
+        f = _file.file(self.temppath, "r")
+        try:
+            s = f.read()
+            assert s == "foobar"
+        finally:
+            f.close()

Modified: pypy/branch/refactor-file/pypy/rlib/streamio.py
==============================================================================
--- pypy/branch/refactor-file/pypy/rlib/streamio.py	(original)
+++ pypy/branch/refactor-file/pypy/rlib/streamio.py	Sat Nov  4 16:35:43 2006
@@ -203,6 +203,9 @@
     def peek(self):
         return ''
 
+    def try_to_find_file_descriptor(self):
+        return -1
+
 
 class DiskFile(Stream):
 
@@ -215,7 +218,8 @@
         os.lseek(self.fd, offset, whence)
 
     def tell(self):
-        return os.lseek(self.fd, 0, 1)
+        #XXX for running on top of the cpy objspace. later we want r_longlong
+        return int(os.lseek(self.fd, 0, 1))
 
     def read(self, n):
         return os.read(self.fd, n)
@@ -235,6 +239,8 @@
         def truncate(self, size):
             os.ftruncate(self.fd, size)
 
+    def try_to_find_file_descriptor(self):
+        return self.fd
 
 # next class is not RPython
 
@@ -329,6 +335,9 @@
     def flush(self):
         self.mm.flush()
 
+    def try_to_find_file_descriptor(self):
+        return self.fd
+
 # ____________________________________________________________
 
 
@@ -583,6 +592,9 @@
     flush      = PassThrough("flush",     flush_buffers=True)
     close      = PassThrough("close",     flush_buffers=False)
 
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
+
 
 class BufferingOutputStream(Stream):
 
@@ -635,6 +647,9 @@
     flush      = PassThrough("flush",    flush_buffers=True)
     close      = PassThrough("close",    flush_buffers=True)
 
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
+
 
 class LineBufferingOutputStream(BufferingOutputStream):
 
@@ -650,6 +665,10 @@
             self.do_write(self.buf[:p])
             self.buf = self.buf[p:]
 
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
+
+
 # ____________________________________________________________
 
 
@@ -680,6 +699,8 @@
     flush    = PassThrough("flush", flush_buffers=False)
     close    = PassThrough("close", flush_buffers=False)
 
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
 
 class TextInputFilter(Stream):
 
@@ -829,6 +850,9 @@
     flush      = PassThrough("flush",     flush_buffers=True)
     close      = PassThrough("close",     flush_buffers=False)
 
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
+
 
 class TextOutputFilter(Stream):
 
@@ -852,6 +876,8 @@
     flush      = PassThrough("flush",     flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
 
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
 
 
 # _________________________________________________
@@ -901,6 +927,8 @@
     flush      = PassThrough("flush",     flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
 
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
 
 class EncodingOutputFilter(Stream):
 
@@ -926,3 +954,7 @@
     truncate   = PassThrough("truncate",  flush_buffers=False)
     flush      = PassThrough("flush",     flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
+
+    def try_to_find_file_descriptor(self):
+        return self.base.try_to_find_file_descriptor()
+



More information about the Pypy-commit mailing list