Index: Include/fileobject.h =================================================================== --- Include/fileobject.h (revision 77913) +++ Include/fileobject.h (working copy) @@ -28,6 +28,8 @@ PyObject *weakreflist; /* List of weak references */ int unlocked_count; /* Num. currently running sections of code using f_fp with the GIL released. */ + int readable; + int writable; } PyFileObject; PyAPI_DATA(PyTypeObject) PyFile_Type; Index: Objects/fileobject.c =================================================================== --- Objects/fileobject.c (revision 77913) +++ Objects/fileobject.c (working copy) @@ -167,6 +167,13 @@ f->f_encoding = Py_None; Py_INCREF(Py_None); f->f_errors = Py_None; + f->readable = f->writable = 0; + if (strchr(mode, 'r') != NULL || f->f_univ_newline) + f->readable = 1; + if (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL) + f->writable = 1; + if (strchr(mode, '+') != NULL) + f->readable = f->writable = 1; if (f->f_mode == NULL) return NULL; @@ -568,6 +575,15 @@ return NULL; } +static PyObject * +err_mode(char *action) +{ + /* fileio.c raises ValueError, but here it is used in + * situations that previously raised IOError. */ + PyErr_Format(PyExc_IOError, "File not open for %s", action); + return NULL; +} + /* Refuse regular file I/O if there's data in the iteration-buffer. * Mixing them would cause data to arrive out of order, as the read* * methods don't use the iteration buffer. */ @@ -782,6 +798,8 @@ if (f->f_fp == NULL) return err_closed(); + if (!f->writable) + return err_mode("writing"); if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj)) return NULL; @@ -1030,6 +1048,8 @@ if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1099,6 +1119,8 @@ if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1470,6 +1492,8 @@ PyFileObject *fo = (PyFileObject *)f; if (fo->f_fp == NULL) return err_closed(); + if (!fo->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (fo->f_buf != NULL && (fo->f_bufend - fo->f_bufptr) > 0 && @@ -1558,6 +1582,8 @@ if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1591,6 +1617,8 @@ if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1709,6 +1737,8 @@ Py_ssize_t n, n2; if (f->f_fp == NULL) return err_closed(); + if (!f->writable) + return err_mode("writing"); if (f->f_binary) { if (!PyArg_ParseTuple(args, "s*", &pbuf)) return NULL; @@ -1746,6 +1776,8 @@ assert(seq != NULL); if (f->f_fp == NULL) return err_closed(); + if (!f->writable) + return err_mode("writing"); result = NULL; list = NULL; @@ -2186,6 +2218,8 @@ if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); l = readahead_get_line_skip(f, 0, READAHEAD_BUFSIZE); if (l == NULL || PyString_GET_SIZE(l) == 0) { Index: Lib/test/test_file2k.py =================================================================== --- Lib/test/test_file2k.py (revision 77913) +++ Lib/test/test_file2k.py (working copy) @@ -86,6 +86,8 @@ self.assertTrue(repr(self.f).startswith("