Index: Modules/_fileio.c =================================================================== --- Modules/_fileio.c (revision 58930) +++ Modules/_fileio.c (working copy) @@ -7,6 +7,8 @@ #include #include /* For offsetof */ +#define INVALID_FD -0xFABC + /* * Known likely problems: * @@ -30,10 +32,11 @@ typedef struct { PyObject_HEAD int fd; + int closed : 1; unsigned readable : 1; unsigned writable : 1; int seekable : 2; /* -1 means unknown */ - int closefd : 1; + int close_fd : 1; PyObject *weakreflist; } PyFileIOObject; @@ -46,9 +49,15 @@ internal_close(PyFileIOObject *self) { int save_errno = 0; + + if (self->closed) { + return save_errno; + } + self->closed = 1; + if (self->fd >= 0) { int fd = self->fd; - self->fd = -1; + self->fd = INVALID_FD; Py_BEGIN_ALLOW_THREADS if (close(fd) < 0) save_errno = errno; @@ -60,7 +69,7 @@ static PyObject * fileio_close(PyFileIOObject *self) { - if (!self->closefd) { + if (!self->close_fd) { if (PyErr_WarnEx(PyExc_RuntimeWarning, "Trying to close unclosable fd!", 3) < 0) { return NULL; @@ -86,6 +95,7 @@ self = (PyFileIOObject *) type->tp_alloc(type, 0); if (self != NULL) { self->fd = -1; + self->closed = 0; self->weakreflist = NULL; } @@ -127,7 +137,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) { PyFileIOObject *self = (PyFileIOObject *) oself; - static char *kwlist[] = {"file", "mode", "closefd", NULL}; + static char *kwlist[] = {"file", "mode", "close_fd", NULL}; char *name = NULL; char *mode = "r"; char *s; @@ -137,8 +147,8 @@ int ret = 0; int rwa = 0, plus = 0, append = 0; int flags = 0; - int fd = -1; - int closefd = 1; + int fd = INVALID_FD; + int close_fd = 1; assert(PyFileIO_Check(oself)); if (self->fd >= 0) { @@ -148,12 +158,12 @@ } if (PyArg_ParseTupleAndKeywords(args, kwds, "i|si:fileio", - kwlist, &fd, &mode, &closefd)) { - if (fd < 0) { + kwlist, &fd, &mode, &close_fd)) { + /*if (fd < 0) { PyErr_SetString(PyExc_ValueError, "Negative filedescriptor"); return -1; - } + }*/ } else { PyErr_Clear(); @@ -163,7 +173,7 @@ /* On NT, so wide API available */ PyObject *po; if (PyArg_ParseTupleAndKeywords(args, kwds, "U|si:fileio", - kwlist, &po, &mode, &closefd) + kwlist, &po, &mode, &close_fd) ) { widename = PyUnicode_AS_UNICODE(po); } else { @@ -178,7 +188,7 @@ if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:fileio", kwlist, Py_FileSystemDefaultEncoding, - &name, &mode, &closefd)) + &name, &mode, &close_fd)) goto error; } } @@ -245,15 +255,15 @@ flags |= O_APPEND; #endif - if (fd >= 0) { + if (fd != INVALID_FD) { self->fd = fd; - self->closefd = closefd; + self->close_fd = close_fd; } else { - self->closefd = 1; - if (!closefd) { + self->close_fd = 1; + if (!close_fd) { PyErr_SetString(PyExc_ValueError, - "Cannot use closefd=True with file name"); + "Cannot use close_fd=True with file name"); goto error; } @@ -292,7 +302,7 @@ if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); - if (self->fd >= 0 && self->closefd) { + if (self->fd >= 0 && self->close_fd) { errno = internal_close(self); if (errno < 0) { #ifdef HAVE_STRERROR @@ -315,16 +325,44 @@ } static PyObject * +err_invalid(void) +{ +#ifdef EBADF + errno = EBADF; + PyErr_SetFromErrno(PyExc_IOError); + return NULL; +#else + PyErr_SetString(PyExc_IOError, "Bad file descriptor"); + return NULL; +#endif +} + +static PyObject * err_mode(char *action) { PyErr_Format(PyExc_ValueError, "File not open for %s", action); return NULL; } +int +err_check(PyFileIOObject *self) +{ + if (self->closed) { + err_closed(); + return 1; + } + if (self->fd < 0) { + err_invalid(); + return 1; + } + return 0; +} + static PyObject * fileio_fileno(PyFileIOObject *self) { - if (self->fd < 0) + /* don't check for valid fd here */ + if (self->closed) return err_closed(); return PyInt_FromLong((long) self->fd); } @@ -332,7 +370,8 @@ static PyObject * fileio_readable(PyFileIOObject *self) { - if (self->fd < 0) + /* don't check for valid fd here */ + if (self->closed) return err_closed(); return PyBool_FromLong((long) self->readable); } @@ -340,7 +379,8 @@ static PyObject * fileio_writable(PyFileIOObject *self) { - if (self->fd < 0) + /* don't check for valid fd here */ + if (self->closed) return err_closed(); return PyBool_FromLong((long) self->writable); } @@ -348,7 +388,8 @@ static PyObject * fileio_seekable(PyFileIOObject *self) { - if (self->fd < 0) + /* don't check for valid fd here */ + if (self->closed) return err_closed(); if (self->seekable < 0) { int ret; @@ -369,8 +410,8 @@ char *ptr; Py_ssize_t n; - if (self->fd < 0) - return err_closed(); + if (err_check(self)) + return NULL; if (!self->readable) return err_mode("reading"); @@ -456,8 +497,8 @@ Py_ssize_t size = -1; PyObject *bytes; - if (self->fd < 0) - return err_closed(); + if (err_check(self)) + return NULL; if (!self->readable) return err_mode("reading"); @@ -501,8 +542,8 @@ Py_ssize_t n; char *ptr; - if (self->fd < 0) - return err_closed(); + if (err_check(self)) + return NULL; if (!self->writable) return err_mode("writing"); @@ -593,8 +634,8 @@ PyObject *posobj; int whence = 0; - if (self->fd < 0) - return err_closed(); + if (err_check(self)) + return NULL; if (!PyArg_ParseTuple(args, "O|i", &posobj, &whence)) return NULL; @@ -605,8 +646,8 @@ static PyObject * fileio_tell(PyFileIOObject *self, PyObject *args) { - if (self->fd < 0) - return err_closed(); + if (err_check(self)) + return NULL; return portable_lseek(self->fd, NULL, 1); } @@ -618,11 +659,9 @@ PyObject *posobj = NULL; Py_off_t pos; int ret; - int fd; - fd = self->fd; - if (fd < 0) - return err_closed(); + if (err_check(self)) + return NULL; if (!self->writable) return err_mode("writing"); @@ -630,7 +669,7 @@ return NULL; if (posobj == Py_None || posobj == NULL) { - posobj = portable_lseek(fd, NULL, 1); + posobj = portable_lseek(self->fd, NULL, 1); if (posobj == NULL) return NULL; } @@ -665,7 +704,7 @@ /* Have to move current pos to desired endpoint on Windows. */ errno = 0; - pos2 = portable_lseek(fd, posobj, SEEK_SET); + pos2 = portable_lseek(self->fd, posobj, SEEK_SET); if (pos2 == NULL) { Py_DECREF(posobj); Py_DECREF(oldposobj); @@ -676,7 +715,7 @@ /* Truncate. Note that this may grow the file! */ Py_BEGIN_ALLOW_THREADS errno = 0; - hFile = (HANDLE)_get_osfhandle(fd); + hFile = (HANDLE)_get_osfhandle(self->fd); ret = hFile == (HANDLE)-1; if (ret == 0) { ret = SetEndOfFile(hFile) == 0; @@ -687,7 +726,7 @@ if (ret == 0) { /* Move to the previous position in the file */ - pos2 = portable_lseek(fd, oldposobj, SEEK_SET); + pos2 = portable_lseek(self->fd, oldposobj, SEEK_SET); if (pos2 == NULL) { Py_DECREF(posobj); Py_DECREF(oldposobj); @@ -700,7 +739,7 @@ #else Py_BEGIN_ALLOW_THREADS errno = 0; - ret = ftruncate(fd, pos); + ret = ftruncate(self->fd, pos); Py_END_ALLOW_THREADS #endif /* !MS_WINDOWS */ @@ -742,8 +781,10 @@ { long res; - if (self->fd < 0) + /* don't check for valid fd here */ + if (self->closed) return err_closed(); + Py_BEGIN_ALLOW_THREADS res = isatty(self->fd); Py_END_ALLOW_THREADS @@ -850,7 +891,7 @@ static PyObject * get_closed(PyFileIOObject *self, void *closure) { - return PyBool_FromLong((long)(self->fd < 0)); + return PyBool_FromLong((long)(self->closed)); } static PyObject * Index: Objects/fileobject.c =================================================================== --- Objects/fileobject.c (revision 58930) +++ Objects/fileobject.c (working copy) @@ -325,7 +325,10 @@ return buf; } -/* **************************** std printer **************************** */ +/* **************************** std printer **************************** + * The stdprinter is used during the boot strapping phase as a preliminary + * file like object for sys.stderr. + */ typedef struct { PyObject_HEAD @@ -347,15 +350,24 @@ return (PyObject *) self; } +static int +fileio_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyErr_SetString(PyExc_TypeError, + "cannot create 'stderrprinter' instances"); + return -1; +} + PyObject * PyFile_NewStdPrinter(int fd) { PyStdPrinter_Object *self; - - if ((fd != fileno(stdout) && fd != fileno(stderr)) || fd < 0) { +#ifndef PY_STDERR_FILE + if (fd != fileno(stdout) && fd != fileno(stderr)) { /* not enough infrastructure for PyErr_BadInternalCall() */ return NULL; } +#endif self = PyObject_New(PyStdPrinter_Object, &PyStdPrinter_Type); @@ -372,14 +384,17 @@ Py_ssize_t n; if (self->fd < 0) { - PyErr_SetString(PyExc_ValueError, - "I/O operation on closed file"); - return NULL; + /* fd might be invalid on Windows + * I can't raise an exception here. It may lead to an + * unlimited recursion in the case stderr is invalid. + */ + return PyLong_FromLong((long)-1); } - if (!PyArg_ParseTuple(args, "s#", &c, &n)) { + if (!PyArg_ParseTuple(args, "s", &c)) { return NULL; } + n = strlen(c); Py_BEGIN_ALLOW_THREADS errno = 0; @@ -393,14 +408,75 @@ return NULL; } - return PyInt_FromSsize_t(n); + return PyLong_FromSsize_t(n); } +static PyObject * +stdprinter_fileno(PyStdPrinter_Object *self) +{ + return PyInt_FromLong((long) self->fd); +} + +static PyObject * +stdprinter_repr(PyStdPrinter_Object *self) +{ + return PyUnicode_FromFormat("", + self->fd, self); +} + +static PyObject * +stdprinter_noop(PyStdPrinter_Object *self) +{ + Py_RETURN_NONE; +} + +static PyObject * +stdprinter_isatty(PyStdPrinter_Object *self) +{ + long res; + + Py_BEGIN_ALLOW_THREADS + res = isatty(self->fd); + Py_END_ALLOW_THREADS + + return PyBool_FromLong(res); +} + static PyMethodDef stdprinter_methods[] = { - {"write", (PyCFunction)stdprinter_write, METH_VARARGS, ""}, - {NULL, NULL} /* sentinel */ + {"close", (PyCFunction)stdprinter_noop, METH_NOARGS, ""}, + {"flush", (PyCFunction)stdprinter_noop, METH_NOARGS, ""}, + {"fileno", (PyCFunction)stdprinter_fileno, METH_NOARGS, ""}, + {"isatty", (PyCFunction)stdprinter_isatty, METH_NOARGS, ""}, + {"write", (PyCFunction)stdprinter_write, METH_VARARGS, ""}, + {NULL, NULL} /*sentinel */ }; +static PyObject * +get_closed(PyStdPrinter_Object *self, void *closure) +{ + Py_INCREF(Py_False); + return Py_False; +} + +static PyObject * +get_mode(PyStdPrinter_Object *self, void *closure) +{ + return PyUnicode_FromString("w"); +} + +static PyObject * +get_encoding(PyStdPrinter_Object *self, void *closure) +{ + Py_RETURN_NONE; +} + +static PyGetSetDef stdprinter_getsetlist[] = { + {"closed", (getter)get_closed, NULL, "True if the file is closed"}, + {"encoding", (getter)get_encoding, NULL, "Encoding of the file"}, + {"mode", (getter)get_mode, NULL, "String giving the file mode"}, + {0}, +}; + PyTypeObject PyStdPrinter_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "stderrprinter", /* tp_name */ @@ -412,7 +488,7 @@ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + (reprfunc)stdprinter_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -432,13 +508,13 @@ 0, /* tp_iternext */ stdprinter_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + stdprinter_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + fileio_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ stdprinter_new, /* tp_new */ PyObject_Del, /* tp_free */ Index: PC/pyconfig.h =================================================================== --- PC/pyconfig.h (revision 58930) +++ PC/pyconfig.h (working copy) @@ -706,4 +706,7 @@ socket handles greater than FD_SETSIZE */ #define Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE +/* Redirect stderr and stdout to a file named stderr.txt */ +/* #define PY_STDERR_FILE 1 */ + #endif /* !Py_CONFIG_H */ Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (revision 58930) +++ Python/pythonrun.c (working copy) @@ -17,7 +17,6 @@ #include "ast.h" #include "eval.h" #include "marshal.h" - #ifdef HAVE_SIGNAL_H #include #endif @@ -40,6 +39,10 @@ _Py_GetRefTotal()) #endif +#ifdef PY_STDERR_FILE +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -152,8 +155,11 @@ { PyInterpreterState *interp; PyThreadState *tstate; - PyObject *bimod, *sysmod, *pstderr; + PyObject *bimod, *sysmod, *pstd; char *p; +#ifdef PY_STDERR_FILE + int fd; +#endif #if defined(HAVE_LANGINFO_H) && defined(CODESET) char *codeset; #endif @@ -231,12 +237,25 @@ /* Set up a preliminary stderr printer until we have enough infrastructure for the io module in place. */ - pstderr = PyFile_NewStdPrinter(fileno(stderr)); - if (pstderr == NULL) +#ifdef PY_STDERR_FILE + fd = open("stderr.txt", O_WRONLY|O_BINARY|O_CREAT|O_TRUNC); + pstd = PyFile_NewStdPrinter(fd); +#else + pstd = PyFile_NewStdPrinter(fileno(stderr)); +#endif + if (pstd == NULL) Py_FatalError("Py_Initialize: can't set preliminary stderr"); - PySys_SetObject("stderr", pstderr); - PySys_SetObject("__stderr__", pstderr); + PySys_SetObject("stderr", pstd); + PySys_SetObject("__stderr__", pstd); +#ifndef PY_STDERR_FILE + pstd = PyFile_NewStdPrinter(fileno(stdout)); +#endif + if (pstd == NULL) + Py_FatalError("Py_Initialize: can't set preliminary stdout"); + PySys_SetObject("stdout", pstd); + PySys_SetObject("__stdout__", pstd); + _PyImport_Init(); /* initialize builtin exceptions */ @@ -756,6 +775,7 @@ PySys_SetObject("stdin", std); Py_DECREF(std); +#if 1 /* Disable this if you have trouble debugging bootstrap stuff */ /* Set sys.stdout */ if (!(std = PyFile_FromFd(fileno(stdout), "", "w", -1, NULL, "\n", 0))) { @@ -765,7 +785,6 @@ PySys_SetObject("stdout", std); Py_DECREF(std); -#if 1 /* Disable this if you have trouble debugging bootstrap stuff */ /* Set sys.stderr, replaces the preliminary stderr */ if (!(std = PyFile_FromFd(fileno(stderr), "", "w", -1, NULL, "\n", 0))) {