diff -r 11dea186d3fc Modules/_io/_iomodule.h --- a/Modules/_io/_iomodule.h Wed Oct 13 15:15:02 2010 +0200 +++ b/Modules/_io/_iomodule.h Wed Oct 13 22:59:32 2010 +0200 @@ -19,7 +19,6 @@ extern PyTypeObject PyBufferedRandom_Typ extern PyTypeObject PyTextIOWrapper_Type; extern PyTypeObject PyIncrementalNewlineDecoder_Type; - extern int _PyIO_ConvertSsize_t(PyObject *, void *); /* These functions are used as METH_NOARGS methods, are normally called @@ -41,6 +40,13 @@ extern int _PyIOBase_finalize(PyObject * Doesn't check the argument type, so be careful! */ extern int _PyFileIO_closed(PyObject *self); +/* Warn if the given RawIO object is not yet closed (used in dealloc + routines, hence the name). No-op if the object isn't a FileIO. */ +extern void _PyFileIO_dealloc_warn(PyObject *self, PyObject *source); + +/* Same with a BufferedIO object. */ +extern void _PyBufferedIO_dealloc_warn(PyObject *self, PyObject *source); + /* Shortcut to the core of the IncrementalNewlineDecoder.decode method */ extern PyObject *_PyIncrementalNewlineDecoder_decode( PyObject *self, PyObject *input, int final); diff -r 11dea186d3fc Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c Wed Oct 13 15:15:02 2010 +0200 +++ b/Modules/_io/bufferedio.c Wed Oct 13 22:59:32 2010 +0200 @@ -197,6 +197,7 @@ typedef struct { int detached; int readable; int writable; + int deallocating; /* True if this is a vanilla Buffered object (rather than a user derived class) *and* the raw stream is a vanilla FileIO object. */ @@ -342,6 +343,7 @@ typedef struct { static void buffered_dealloc(buffered *self) { + self->deallocating = 1; if (self->ok && _PyIOBase_finalize((PyObject *) self) < 0) return; _PyObject_GC_UNTRACK(self); @@ -435,6 +437,9 @@ buffered_close(buffered *self, PyObject Py_INCREF(res); goto end; } + + if (self->deallocating) + _PyBufferedIO_dealloc_warn((PyObject *) self, (PyObject *) self); /* flush() will most probably re-take the lock, so drop it first */ LEAVE_BUFFERED(self) res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); @@ -2297,3 +2302,16 @@ PyTypeObject PyBufferedRandom_Type = { PyType_GenericNew, /* tp_new */ }; + +void +_PyBufferedIO_dealloc_warn(PyObject *obj, PyObject *source) +{ + if (PyObject_TypeCheck(obj, &PyBufferedReader_Type) || + PyObject_TypeCheck(obj, &PyBufferedWriter_Type) || + PyObject_TypeCheck(obj, &PyBufferedRandom_Type)) { + buffered *self = (buffered *) obj; + if (self->raw) { + _PyFileIO_dealloc_warn(self->raw, source); + } + } +} diff -r 11dea186d3fc Modules/_io/fileio.c --- a/Modules/_io/fileio.c Wed Oct 13 15:15:02 2010 +0200 +++ b/Modules/_io/fileio.c Wed Oct 13 22:59:32 2010 +0200 @@ -2,6 +2,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "structmember.h" #ifdef HAVE_SYS_TYPES_H #include #endif @@ -55,6 +56,7 @@ typedef struct { unsigned int writable : 1; signed int seekable : 2; /* -1 means unknown */ unsigned int closefd : 1; + unsigned int deallocating: 1; PyObject *weakreflist; PyObject *dict; } fileio; @@ -69,6 +71,24 @@ _PyFileIO_closed(PyObject *self) return ((fileio *)self)->fd < 0; } +void +_PyFileIO_dealloc_warn(PyObject *obj, PyObject *source) +{ + fileio *self; + + if (!PyFileIO_Check(obj)) + return; + self = (fileio *) obj; + if (self->fd >= 0 && self->closefd) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + PyErr_SetString(PyExc_RuntimeWarning, + "file not yet closed when deallocating"); + PyErr_WriteUnraisable(source); + PyErr_Restore(exc, val, tb); + } +} + static PyObject * portable_lseek(int fd, PyObject *posobj, int whence); @@ -110,6 +130,8 @@ fileio_close(fileio *self) self->fd = -1; Py_RETURN_NONE; } + if (self->deallocating) + _PyFileIO_dealloc_warn((PyObject *) self, (PyObject *) self); errno = internal_close(self); if (errno < 0) return NULL; @@ -398,6 +420,7 @@ fileio_clear(fileio *self) static void fileio_dealloc(fileio *self) { + self->deallocating = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; _PyObject_GC_UNTRACK(self); diff -r 11dea186d3fc Modules/_io/textio.c --- a/Modules/_io/textio.c Wed Oct 13 15:15:02 2010 +0200 +++ b/Modules/_io/textio.c Wed Oct 13 22:59:32 2010 +0200 @@ -658,6 +658,7 @@ typedef struct char writetranslate; char seekable; char telling; + char deallocating; /* Specialized encoding func (see below) */ encodefunc_t encodefunc; /* Whether or not it's the start of the stream */ @@ -1094,6 +1095,7 @@ _textiowrapper_clear(textio *self) static void textiowrapper_dealloc(textio *self) { + self->deallocating = 1; if (_textiowrapper_clear(self) < 0) return; _PyObject_GC_UNTRACK(self); @@ -2410,6 +2412,8 @@ textiowrapper_close(textio *self, PyObje Py_RETURN_NONE; /* stream already closed */ } else { + if (self->deallocating) + _PyBufferedIO_dealloc_warn(self->buffer, (PyObject *) self); res = PyObject_CallMethod((PyObject *)self, "flush", NULL); if (res == NULL) { return NULL;