[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