Implement long positioning (Unix only, probably).

Etc., etc.
This commit is contained in:
Guido van Rossum 2007-04-10 19:01:47 +00:00
parent cce92b27d6
commit 53807dabf0
3 changed files with 210 additions and 179 deletions

View File

@ -1,9 +1,15 @@
"""New I/O library. """New I/O library conforming to PEP 3116.
This is an early prototype; eventually some of this will be This is an early prototype; eventually some of this will be
reimplemented in C and the rest may be turned into a package. reimplemented in C and the rest may be turned into a package.
See PEP 3116. Conformance of alternative implementations: all arguments are intended
to be positional-only except the arguments of the open() function.
Argument names except those of the open() function are not part of the
specification. Instance variables and methods whose name starts with
a leading underscore are not part of the specification (except "magic"
names like __iter__). Only the top-level names listed in the __all__
variable are part of the specification.
XXX need to default buffer size to 1 if isatty() XXX need to default buffer size to 1 if isatty()
XXX need to support 1 meaning line-buffered XXX need to support 1 meaning line-buffered
@ -142,6 +148,9 @@ class IOBase:
This does not define read(), readinto() and write(), nor This does not define read(), readinto() and write(), nor
readline() and friends, since their signatures vary per layer. readline() and friends, since their signatures vary per layer.
Not that calling any method (even inquiries) on a closed file is
undefined. Implementations may raise IOError in this case.
""" """
### Internal ### ### Internal ###
@ -153,19 +162,20 @@ class IOBase:
### Positioning ### ### Positioning ###
def seek(self, pos: int, whence: int = 0) -> None: def seek(self, pos: int, whence: int = 0) -> int:
"""seek(pos: int, whence: int = 0) -> None. Change stream position. """seek(pos: int, whence: int = 0) -> int. Change stream position.
Seek to byte offset pos relative to position indicated by whence: Seek to byte offset pos relative to position indicated by whence:
0 Start of stream (the default). pos should be >= 0; 0 Start of stream (the default). pos should be >= 0;
1 Current position - whence may be negative; 1 Current position - whence may be negative;
2 End of stream - whence usually negative. 2 End of stream - whence usually negative.
Returns the new absolute position.
""" """
self._unsupported("seek") self._unsupported("seek")
def tell(self) -> int: def tell(self) -> int:
"""tell() -> int. Return current stream position.""" """tell() -> int. Return current stream position."""
self._unsupported("tell") return self.seek(0, 1)
def truncate(self, pos: int = None) -> None: def truncate(self, pos: int = None) -> None:
"""truncate(size: int = None) -> None. Truncate file to size bytes. """truncate(size: int = None) -> None. Truncate file to size bytes.
@ -432,7 +442,7 @@ class _BufferedIOMixin(BufferedIOBase):
### Positioning ### ### Positioning ###
def seek(self, pos, whence=0): def seek(self, pos, whence=0):
self.raw.seek(pos, whence) return self.raw.seek(pos, whence)
def tell(self): def tell(self):
return self.raw.tell() return self.raw.tell()
@ -515,6 +525,7 @@ class _MemoryIOMixin(BufferedIOBase):
self._pos = max(0, len(self._buffer) + pos) self._pos = max(0, len(self._buffer) + pos)
else: else:
raise IOError("invalid whence value") raise IOError("invalid whence value")
return self._pos
def tell(self): def tell(self):
return self._pos return self._pos
@ -620,8 +631,9 @@ class BufferedReader(_BufferedIOMixin):
def seek(self, pos, whence=0): def seek(self, pos, whence=0):
if whence == 1: if whence == 1:
pos -= len(self._read_buf) pos -= len(self._read_buf)
self.raw.seek(pos, whence) pos = self.raw.seek(pos, whence)
self._read_buf = b"" self._read_buf = b""
return pos
class BufferedWriter(_BufferedIOMixin): class BufferedWriter(_BufferedIOMixin):
@ -679,7 +691,7 @@ class BufferedWriter(_BufferedIOMixin):
def seek(self, pos, whence=0): def seek(self, pos, whence=0):
self.flush() self.flush()
self.raw.seek(pos, whence) return self.raw.seek(pos, whence)
class BufferedRWPair(BufferedIOBase): class BufferedRWPair(BufferedIOBase):
@ -750,13 +762,9 @@ class BufferedRandom(BufferedWriter, BufferedReader):
self.flush() self.flush()
# First do the raw seek, then empty the read buffer, so that # First do the raw seek, then empty the read buffer, so that
# if the raw seek fails, we don't lose buffered data forever. # if the raw seek fails, we don't lose buffered data forever.
self.raw.seek(pos, whence) pos = self.raw.seek(pos, whence)
self._read_buf = b"" self._read_buf = b""
# XXX I suppose we could implement some magic here to move through the return pos
# existing read buffer in the case of seek(<some small +ve number>, 1)
# XXX OTOH it might be good to *guarantee* that the buffer is
# empty after a seek or flush; for small relative forward
# seeks one might as well use small reads instead.
def tell(self): def tell(self):
if (self._write_buf): if (self._write_buf):

View File

@ -4,10 +4,10 @@ import unittest
from itertools import chain from itertools import chain
from test import test_support from test import test_support
import io import io # The module under test
class MockIO(io.RawIOBase): class MockRawIO(io.RawIOBase):
def __init__(self, read_stack=()): def __init__(self, read_stack=()):
self._read_stack = list(read_stack) self._read_stack = list(read_stack)
@ -56,13 +56,13 @@ class MockFileIO(io.BytesIO):
class MockNonBlockWriterIO(io.RawIOBase): class MockNonBlockWriterIO(io.RawIOBase):
def __init__(self, blockingScript): def __init__(self, blocking_script):
self.bs = list(blockingScript) self._blocking_script = list(blocking_script)
self._write_stack = [] self._write_stack = []
def write(self, b): def write(self, b):
self._write_stack.append(b[:]) self._write_stack.append(b[:])
n = self.bs.pop(0) n = self._blocking_script.pop(0)
if (n < 0): if (n < 0):
raise io.BlockingIOError(0, "test blocking", -n) raise io.BlockingIOError(0, "test blocking", -n)
else: else:
@ -90,6 +90,23 @@ class IOTest(unittest.TestCase):
f.seek(-2, 2) f.seek(-2, 2)
f.truncate() f.truncate()
def large_file_ops(self, f):
assert f.readable()
assert f.writable()
self.assertEqual(f.seek(2**32), 2**32)
self.assertEqual(f.tell(), 2**32)
self.assertEqual(f.write(b"xxx"), 3)
self.assertEqual(f.tell(), 2**32 + 3)
self.assertEqual(f.seek(-1, 1), 2**32 + 2)
f.truncate()
self.assertEqual(f.tell(), 2**32 + 2)
self.assertEqual(f.seek(0, 2), 2**32 + 2)
f.truncate(2**32 + 1)
self.assertEqual(f.tell(), 2**32 + 1)
self.assertEqual(f.seek(0, 2), 2**32 + 1)
self.assertEqual(f.seek(-1, 2), 2**32)
self.assertEqual(f.read(2), b"x")
def read_ops(self, f): def read_ops(self, f):
data = f.read(5) data = f.read(5)
self.assertEqual(data, b"hello") self.assertEqual(data, b"hello")
@ -130,19 +147,9 @@ class IOTest(unittest.TestCase):
f = io.BytesIO(data) f = io.BytesIO(data)
self.read_ops(f) self.read_ops(f)
def test_fileio_FileIO(self): def test_large_file_ops(self):
import _fileio f = io.open(test_support.TESTFN, "w+b", buffering=0)
f = _fileio._FileIO(test_support.TESTFN, "w") self.large_file_ops(f)
self.assertEqual(f.readable(), False)
self.assertEqual(f.writable(), True)
self.assertEqual(f.seekable(), True)
self.write_ops(f)
f.close()
f = _fileio._FileIO(test_support.TESTFN, "r")
self.assertEqual(f.readable(), True)
self.assertEqual(f.writable(), False)
self.assertEqual(f.seekable(), True)
self.read_ops(f)
f.close() f.close()
@ -205,7 +212,7 @@ class StringIOTest(MemorySeekTestMixin, unittest.TestCase):
class BufferedReaderTest(unittest.TestCase): class BufferedReaderTest(unittest.TestCase):
def testRead(self): def testRead(self):
rawio = MockIO((b"abc", b"d", b"efg")) rawio = MockRawIO((b"abc", b"d", b"efg"))
bufio = io.BufferedReader(rawio) bufio = io.BufferedReader(rawio)
self.assertEquals(b"abcdef", bufio.read(6)) self.assertEquals(b"abcdef", bufio.read(6))
@ -231,7 +238,7 @@ class BufferedReaderTest(unittest.TestCase):
def testReadNonBlocking(self): def testReadNonBlocking(self):
# Inject some None's in there to simulate EWOULDBLOCK # Inject some None's in there to simulate EWOULDBLOCK
rawio = MockIO((b"abc", b"d", None, b"efg", None, None)) rawio = MockRawIO((b"abc", b"d", None, b"efg", None, None))
bufio = io.BufferedReader(rawio) bufio = io.BufferedReader(rawio)
self.assertEquals(b"abcd", bufio.read(6)) self.assertEquals(b"abcd", bufio.read(6))
@ -241,19 +248,19 @@ class BufferedReaderTest(unittest.TestCase):
self.assertEquals(b"", bufio.read()) self.assertEquals(b"", bufio.read())
def testReadToEof(self): def testReadToEof(self):
rawio = MockIO((b"abc", b"d", b"efg")) rawio = MockRawIO((b"abc", b"d", b"efg"))
bufio = io.BufferedReader(rawio) bufio = io.BufferedReader(rawio)
self.assertEquals(b"abcdefg", bufio.read(9000)) self.assertEquals(b"abcdefg", bufio.read(9000))
def testReadNoArgs(self): def testReadNoArgs(self):
rawio = MockIO((b"abc", b"d", b"efg")) rawio = MockRawIO((b"abc", b"d", b"efg"))
bufio = io.BufferedReader(rawio) bufio = io.BufferedReader(rawio)
self.assertEquals(b"abcdefg", bufio.read()) self.assertEquals(b"abcdefg", bufio.read())
def testFileno(self): def testFileno(self):
rawio = MockIO((b"abc", b"d", b"efg")) rawio = MockRawIO((b"abc", b"d", b"efg"))
bufio = io.BufferedReader(rawio) bufio = io.BufferedReader(rawio)
self.assertEquals(42, bufio.fileno()) self.assertEquals(42, bufio.fileno())
@ -268,7 +275,7 @@ class BufferedWriterTest(unittest.TestCase):
def testWrite(self): def testWrite(self):
# Write to the buffered IO but don't overflow the buffer. # Write to the buffered IO but don't overflow the buffer.
writer = MockIO() writer = MockRawIO()
bufio = io.BufferedWriter(writer, 8) bufio = io.BufferedWriter(writer, 8)
bufio.write(b"abc") bufio.write(b"abc")
@ -276,7 +283,7 @@ class BufferedWriterTest(unittest.TestCase):
self.assertFalse(writer._write_stack) self.assertFalse(writer._write_stack)
def testWriteOverflow(self): def testWriteOverflow(self):
writer = MockIO() writer = MockRawIO()
bufio = io.BufferedWriter(writer, 8) bufio = io.BufferedWriter(writer, 8)
bufio.write(b"abc") bufio.write(b"abc")
@ -305,13 +312,13 @@ class BufferedWriterTest(unittest.TestCase):
# later. # later.
def testFileno(self): def testFileno(self):
rawio = MockIO((b"abc", b"d", b"efg")) rawio = MockRawIO((b"abc", b"d", b"efg"))
bufio = io.BufferedWriter(rawio) bufio = io.BufferedWriter(rawio)
self.assertEquals(42, bufio.fileno()) self.assertEquals(42, bufio.fileno())
def testFlush(self): def testFlush(self):
writer = MockIO() writer = MockRawIO()
bufio = io.BufferedWriter(writer, 8) bufio = io.BufferedWriter(writer, 8)
bufio.write(b"abc") bufio.write(b"abc")
@ -323,8 +330,8 @@ class BufferedWriterTest(unittest.TestCase):
class BufferedRWPairTest(unittest.TestCase): class BufferedRWPairTest(unittest.TestCase):
def testRWPair(self): def testRWPair(self):
r = MockIO(()) r = MockRawIO(())
w = MockIO() w = MockRawIO()
pair = io.BufferedRWPair(r, w) pair = io.BufferedRWPair(r, w)
# XXX need implementation # XXX need implementation
@ -333,7 +340,7 @@ class BufferedRWPairTest(unittest.TestCase):
class BufferedRandomTest(unittest.TestCase): class BufferedRandomTest(unittest.TestCase):
def testReadAndWrite(self): def testReadAndWrite(self):
raw = MockIO((b"asdf", b"ghjk")) raw = MockRawIO((b"asdf", b"ghjk"))
rw = io.BufferedRandom(raw, 8, 12) rw = io.BufferedRandom(raw, 8, 12)
self.assertEqual(b"as", rw.read(2)) self.assertEqual(b"as", rw.read(2))

View File

@ -18,12 +18,6 @@
* To Do: * To Do:
* *
* - autoconfify header file inclusion * - autoconfify header file inclusion
* - Make the ABC RawIO type and inherit from it.
*
* Unanswered questions:
*
* - Add mode and name properties a la Python 2 file objects?
* - Check for readable/writable before attempting to read/write?
*/ */
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -36,7 +30,6 @@
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
int fd; int fd;
int own_fd; /* 1 means we should close fd */
int readable; int readable;
int writable; int writable;
int seekable; /* -1 means unknown */ int seekable; /* -1 means unknown */
@ -80,8 +73,6 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kews)
if (self != NULL) { if (self != NULL) {
self->fd = -1; self->fd = -1;
self->weakreflist = NULL; self->weakreflist = NULL;
self->own_fd = 1;
self->seekable = -1;
} }
return (PyObject *) self; return (PyObject *) self;
@ -107,7 +98,7 @@ dircheck(PyFileIOObject* self)
PyObject *exc; PyObject *exc;
PyObject *closeresult = fileio_close(self); PyObject *closeresult = fileio_close(self);
Py_DECREF(closeresult); Py_DECREF(closeresult);
exc = PyObject_CallFunction(PyExc_IOError, "(is)", exc = PyObject_CallFunction(PyExc_IOError, "(is)",
EISDIR, msg); EISDIR, msg);
PyErr_SetObject(PyExc_IOError, exc); PyErr_SetObject(PyExc_IOError, exc);
@ -126,7 +117,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"file", "mode", NULL}; static char *kwlist[] = {"file", "mode", NULL};
char *name = NULL; char *name = NULL;
char *mode = "r"; char *mode = "r";
char *s; char *s;
int wideargument = 0; int wideargument = 0;
int ret = 0; int ret = 0;
int rwa = 0, plus = 0, append = 0; int rwa = 0, plus = 0, append = 0;
@ -183,7 +174,8 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
} }
self->readable = self->writable = 0; self->readable = self->writable = 0;
s = mode; self->seekable = -1;
s = mode;
while (*s) { while (*s) {
switch (*s++) { switch (*s++) {
case 'r': case 'r':
@ -240,7 +232,6 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
if (fd >= 0) { if (fd >= 0) {
self->fd = fd; self->fd = fd;
/* XXX Should we set self->own_fd = 0 ??? */
} }
else { else {
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
@ -257,7 +248,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
error: error:
ret = -1; ret = -1;
done: done:
PyMem_Free(name); PyMem_Free(name);
return ret; return ret;
@ -269,15 +260,16 @@ fileio_dealloc(PyFileIOObject *self)
if (self->weakreflist != NULL) if (self->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) self); PyObject_ClearWeakRefs((PyObject *) self);
if (self->fd >= 0 && self->own_fd) { if (self->fd >= 0) {
PyObject *closeresult = fileio_close(self); PyObject *closeresult = fileio_close(self);
if (closeresult == NULL) { if (closeresult == NULL) {
#ifdef HAVE_STRERROR #ifdef HAVE_STRERROR
PySys_WriteStderr("close failed: [Errno %d] %s\n", errno, strerror(errno)); PySys_WriteStderr("close failed: [Errno %d] %s\n",
errno, strerror(errno));
#else #else
PySys_WriteStderr("close failed: [Errno %d]\n", errno); PySys_WriteStderr("close failed: [Errno %d]\n", errno);
#endif #endif
} else } else
Py_DECREF(closeresult); Py_DECREF(closeresult);
} }
@ -291,6 +283,13 @@ err_closed(void)
return NULL; return NULL;
} }
static PyObject *
err_mode(char *action)
{
PyErr_Format(PyExc_ValueError, "File not open for %s", action);
return NULL;
}
static PyObject * static PyObject *
fileio_fileno(PyFileIOObject *self) fileio_fileno(PyFileIOObject *self)
{ {
@ -338,9 +337,12 @@ fileio_readinto(PyFileIOObject *self, PyObject *args)
{ {
char *ptr; char *ptr;
Py_ssize_t n; Py_ssize_t n;
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
if (!self->readable)
return err_mode("reading");
if (!PyArg_ParseTuple(args, "w#", &ptr, &n)) if (!PyArg_ParseTuple(args, "w#", &ptr, &n))
return NULL; return NULL;
@ -367,6 +369,8 @@ fileio_read(PyFileIOObject *self, PyObject *args)
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
if (!self->readable)
return err_mode("reading");
if (!PyArg_ParseTuple(args, "i", &size)) if (!PyArg_ParseTuple(args, "i", &size))
return NULL; return NULL;
@ -391,9 +395,9 @@ fileio_read(PyFileIOObject *self, PyObject *args)
if (n != size) { if (n != size) {
if (PyBytes_Resize(bytes, n) < 0) { if (PyBytes_Resize(bytes, n) < 0) {
Py_DECREF(bytes); Py_DECREF(bytes);
return NULL; return NULL;
} }
} }
return (PyObject *) bytes; return (PyObject *) bytes;
} }
@ -406,6 +410,9 @@ fileio_write(PyFileIOObject *self, PyObject *args)
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
if (!self->writable)
return err_mode("writing");
if (!PyArg_ParseTuple(args, "s#", &ptr, &n)) if (!PyArg_ParseTuple(args, "s#", &ptr, &n))
return NULL; return NULL;
@ -424,146 +431,163 @@ fileio_write(PyFileIOObject *self, PyObject *args)
return PyInt_FromLong(n); return PyInt_FromLong(n);
} }
/* XXX Windows support below is likely incomplete */
#if defined(MS_WIN64) || defined(MS_WINDOWS)
typedef PY_LONG_LONG Py_off_t;
#else
typedef off_t Py_off_t;
#endif
/* Cribbed from posix_lseek() */
static PyObject *
portable_lseek(int fd, PyObject *posobj, int whence)
{
Py_off_t pos, res;
#ifdef SEEK_SET
/* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
switch (whence) {
#if SEEK_SET != 0
case 0: whence = SEEK_SET; break;
#endif
#if SEEK_CUR != 1
case 1: whence = SEEK_CUR; break;
#endif
#if SEEL_END != 2
case 2: whence = SEEK_END; break;
#endif
}
#endif /* SEEK_SET */
if (posobj == NULL)
pos = 0;
else {
#if !defined(HAVE_LARGEFILE_SUPPORT)
pos = PyInt_AsLong(posobj);
#else
pos = PyLong_Check(posobj) ?
PyLong_AsLongLong(posobj) : PyInt_AsLong(posobj);
#endif
if (PyErr_Occurred())
return NULL;
}
Py_BEGIN_ALLOW_THREADS
#if defined(MS_WIN64) || defined(MS_WINDOWS)
res = _lseeki64(fd, pos, whence);
#else
res = lseek(fd, pos, whence);
#endif
Py_END_ALLOW_THREADS
if (res < 0)
return PyErr_SetFromErrno(PyExc_IOError);
#if !defined(HAVE_LARGEFILE_SUPPORT)
return PyInt_FromLong(res);
#else
return PyLong_FromLongLong(res);
#endif
}
static PyObject * static PyObject *
fileio_seek(PyFileIOObject *self, PyObject *args) fileio_seek(PyFileIOObject *self, PyObject *args)
{ {
Py_ssize_t offset; PyObject *posobj;
Py_ssize_t whence = 0; int whence = 0;
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) if (!PyArg_ParseTuple(args, "O|i", &posobj, &whence))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS return portable_lseek(self->fd, posobj, whence);
errno = 0;
offset = lseek(self->fd, offset, whence);
Py_END_ALLOW_THREADS
if (offset < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
Py_RETURN_NONE;
} }
static PyObject * static PyObject *
fileio_tell(PyFileIOObject *self, PyObject *args) fileio_tell(PyFileIOObject *self, PyObject *args)
{ {
Py_ssize_t offset;
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
Py_BEGIN_ALLOW_THREADS return portable_lseek(self->fd, NULL, 1);
errno = 0;
offset = lseek(self->fd, 0, SEEK_CUR);
Py_END_ALLOW_THREADS
if (offset < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
return PyInt_FromLong(offset);
} }
#ifdef HAVE_FTRUNCATE
static PyObject * static PyObject *
fileio_truncate(PyFileIOObject *self, PyObject *args) fileio_truncate(PyFileIOObject *self, PyObject *args)
{ {
Py_ssize_t length; PyObject *posobj = NULL;
int ret; Py_off_t pos;
int fd, whence;
if (self->fd < 0) fd = self->fd;
if (fd < 0)
return err_closed(); return err_closed();
if (!self->writable)
return err_mode("writing");
if (!PyArg_ParseTuple(args, "|O", &posobj))
return NULL;
if (posobj == NULL)
whence = 1;
else
whence = 0;
posobj = portable_lseek(fd, posobj, whence);
if (posobj == NULL)
return NULL;
#if !defined(HAVE_LARGEFILE_SUPPORT)
pos = PyInt_AsLong(posobj);
#else
pos = PyLong_Check(posobj) ?
PyLong_AsLongLong(posobj) : PyInt_AsLong(posobj);
#endif
Py_DECREF(posobj);
if (PyErr_Occurred())
return NULL;
/* Setup default value */
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
errno = 0; errno = 0;
length = lseek(self->fd, 0, SEEK_CUR); pos = ftruncate(fd, pos);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (length < 0) { if (errno < 0)
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
if (!PyArg_ParseTuple(args, "|i", &length))
return NULL;
#ifdef MS_WINDOWS
/* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
so don't even try using it. */
{
HANDLE hFile;
Py_ssize_t initialpos;
/* Have to move current pos to desired endpoint on Windows. */
Py_BEGIN_ALLOW_THREADS
errno = 0;
ret = _portable_fseek(f->f_fp, newsize, SEEK_SET) != 0;
Py_END_ALLOW_THREADS
if (ret)
goto onioerror;
/* Truncate. Note that this may grow the file! */
Py_BEGIN_ALLOW_THREADS
errno = 0;
hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp));
ret = hFile == (HANDLE)-1;
if (ret == 0) {
ret = SetEndOfFile(hFile) == 0;
if (ret)
errno = EACCES;
}
Py_END_ALLOW_THREADS
if (ret)
goto onioerror;
}
#else
Py_BEGIN_ALLOW_THREADS
errno = 0;
ret = ftruncate(self->fd, length);
Py_END_ALLOW_THREADS
#endif /* !MS_WINDOWS */
if (ret < 0) {
onioerror:
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
/* Return to initial position */
Py_BEGIN_ALLOW_THREADS
errno = 0;
ret = lseek(self->fd, length, SEEK_SET);
Py_END_ALLOW_THREADS
if (ret < 0)
goto onioerror;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#endif
static char *
mode_string(PyFileIOObject *self)
{
if (self->readable) {
if (self->writable)
return "r+";
else
return "r";
}
else
return "w";
}
static PyObject * static PyObject *
fileio_repr(PyFileIOObject *self) fileio_repr(PyFileIOObject *self)
{ {
PyObject *ret = NULL; if (self->fd < 0)
return PyString_FromFormat("_fileio._FileIO(-1)");
ret = PyString_FromFormat("<%s file at %p>", return PyString_FromFormat("_fileio._FileIO(%d, '%s')",
self->fd < 0 ? "closed" : "open", self->fd, mode_string(self));
self);
return ret;
} }
static PyObject * static PyObject *
fileio_isatty(PyFileIOObject *self) fileio_isatty(PyFileIOObject *self)
{ {
long res; long res;
if (self->fd < 0) if (self->fd < 0)
return err_closed(); return err_closed();
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
@ -572,14 +596,6 @@ fileio_isatty(PyFileIOObject *self)
return PyBool_FromLong(res); return PyBool_FromLong(res);
} }
static PyObject *
fileio_self(PyFileIOObject *self)
{
if (self->fd < 0)
return err_closed();
Py_INCREF(self);
return (PyObject *)self;
}
PyDoc_STRVAR(fileio_doc, PyDoc_STRVAR(fileio_doc,
"file(name: str[, mode: str]) -> file IO object\n" "file(name: str[, mode: str]) -> file IO object\n"
@ -639,12 +655,6 @@ PyDoc_STRVAR(close_doc,
PyDoc_STRVAR(isatty_doc, PyDoc_STRVAR(isatty_doc,
"isatty() -> bool. True if the file is connected to a tty device."); "isatty() -> bool. True if the file is connected to a tty device.");
PyDoc_STRVAR(enter_doc,
"__enter__() -> self.");
PyDoc_STRVAR(exit_doc,
"__exit__(*excinfo) -> None. Closes the file.");
PyDoc_STRVAR(seekable_doc, PyDoc_STRVAR(seekable_doc,
"seekable() -> bool. True if file supports random-access."); "seekable() -> bool. True if file supports random-access.");
@ -667,20 +677,26 @@ static PyMethodDef fileio_methods[] = {
{"writable", (PyCFunction)fileio_writable, METH_NOARGS, writable_doc}, {"writable", (PyCFunction)fileio_writable, METH_NOARGS, writable_doc},
{"fileno", (PyCFunction)fileio_fileno, METH_NOARGS, fileno_doc}, {"fileno", (PyCFunction)fileio_fileno, METH_NOARGS, fileno_doc},
{"isatty", (PyCFunction)fileio_isatty, METH_NOARGS, isatty_doc}, {"isatty", (PyCFunction)fileio_isatty, METH_NOARGS, isatty_doc},
{"__enter__",(PyCFunction)fileio_self, METH_NOARGS, enter_doc},
{"__exit__", (PyCFunction)fileio_close, METH_VARARGS, exit_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* 'closed' is an attribute for backwards compatibility reasons. */ /* 'closed' and 'mode' are attributes for backwards compatibility reasons. */
static PyObject * static PyObject *
get_closed(PyFileIOObject *f, void *closure) get_closed(PyFileIOObject *self, void *closure)
{ {
return PyBool_FromLong((long)(f->fd < 0)); return PyBool_FromLong((long)(self->fd < 0));
}
static PyObject *
get_mode(PyFileIOObject *self, void *closure)
{
return PyString_FromString(mode_string(self));
} }
static PyGetSetDef fileio_getsetlist[] = { static PyGetSetDef fileio_getsetlist[] = {
{"closed", (getter)get_closed, NULL, "True if the file is closed"}, {"closed", (getter)get_closed, NULL, "True if the file is closed"},
{"mode", (getter)get_mode, NULL, "String giving the file mode"},
{0}, {0},
}; };