Issue #10093: ResourceWarnings are now issued when files and sockets are

deallocated without explicit closing.  These warnings are silenced by
default, except in pydebug mode.
This commit is contained in:
Antoine Pitrou 2010-10-29 10:38:18 +00:00
parent 9cbdd75ec5
commit e033e06db0
10 changed files with 189 additions and 25 deletions

View file

@ -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);
@ -382,6 +384,23 @@ buffered_clear(buffered *self)
return 0;
}
/* Because this can call arbitrary code, it shouldn't be called when
the refcount is 0 (that is, not directly from tp_dealloc unless
the refcount has been temporarily re-incremented). */
PyObject *
buffered_dealloc_warn(buffered *self, PyObject *source)
{
if (self->ok && self->raw) {
PyObject *r;
r = PyObject_CallMethod(self->raw, "_dealloc_warn", "O", source);
if (r)
Py_DECREF(r);
else
PyErr_Clear();
}
Py_RETURN_NONE;
}
/*
* _BufferedIOMixin methods
* This is not a class, just a collection of methods that will be reused
@ -435,6 +454,14 @@ buffered_close(buffered *self, PyObject *args)
Py_INCREF(res);
goto end;
}
if (self->deallocating) {
PyObject *r = buffered_dealloc_warn(self, (PyObject *) self);
if (r)
Py_DECREF(r);
else
PyErr_Clear();
}
/* flush() will most probably re-take the lock, so drop it first */
LEAVE_BUFFERED(self)
res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
@ -1461,6 +1488,7 @@ static PyMethodDef bufferedreader_methods[] = {
{"writable", (PyCFunction)buffered_writable, METH_NOARGS},
{"fileno", (PyCFunction)buffered_fileno, METH_NOARGS},
{"isatty", (PyCFunction)buffered_isatty, METH_NOARGS},
{"_dealloc_warn", (PyCFunction)buffered_dealloc_warn, METH_O},
{"read", (PyCFunction)buffered_read, METH_VARARGS},
{"peek", (PyCFunction)buffered_peek, METH_VARARGS},
@ -1843,6 +1871,7 @@ static PyMethodDef bufferedwriter_methods[] = {
{"writable", (PyCFunction)buffered_writable, METH_NOARGS},
{"fileno", (PyCFunction)buffered_fileno, METH_NOARGS},
{"isatty", (PyCFunction)buffered_isatty, METH_NOARGS},
{"_dealloc_warn", (PyCFunction)buffered_dealloc_warn, METH_O},
{"write", (PyCFunction)bufferedwriter_write, METH_VARARGS},
{"truncate", (PyCFunction)buffered_truncate, METH_VARARGS},
@ -2227,6 +2256,7 @@ static PyMethodDef bufferedrandom_methods[] = {
{"writable", (PyCFunction)buffered_writable, METH_NOARGS},
{"fileno", (PyCFunction)buffered_fileno, METH_NOARGS},
{"isatty", (PyCFunction)buffered_isatty, METH_NOARGS},
{"_dealloc_warn", (PyCFunction)buffered_dealloc_warn, METH_O},
{"flush", (PyCFunction)buffered_flush, METH_NOARGS},
@ -2296,4 +2326,3 @@ PyTypeObject PyBufferedRandom_Type = {
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};

View file

@ -2,6 +2,7 @@
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "structmember.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#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,26 @@ _PyFileIO_closed(PyObject *self)
return ((fileio *)self)->fd < 0;
}
/* Because this can call arbitrary code, it shouldn't be called when
the refcount is 0 (that is, not directly from tp_dealloc unless
the refcount has been temporarily re-incremented). */
static PyObject *
fileio_dealloc_warn(fileio *self, PyObject *source)
{
if (self->fd >= 0 && self->closefd) {
PyObject *exc, *val, *tb;
PyErr_Fetch(&exc, &val, &tb);
if (PyErr_WarnFormat(PyExc_ResourceWarning, 1,
"unclosed file %R", source)) {
/* Spurious errors can appear at shutdown */
if (PyErr_ExceptionMatches(PyExc_Warning))
PyErr_WriteUnraisable((PyObject *) self);
}
PyErr_Restore(exc, val, tb);
}
Py_RETURN_NONE;
}
static PyObject *
portable_lseek(int fd, PyObject *posobj, int whence);
@ -110,6 +132,13 @@ fileio_close(fileio *self)
self->fd = -1;
Py_RETURN_NONE;
}
if (self->deallocating) {
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
if (r)
Py_DECREF(r);
else
PyErr_Clear();
}
errno = internal_close(self);
if (errno < 0)
return NULL;
@ -399,6 +428,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);
@ -1008,6 +1038,7 @@ static PyMethodDef fileio_methods[] = {
{"writable", (PyCFunction)fileio_writable, METH_NOARGS, writable_doc},
{"fileno", (PyCFunction)fileio_fileno, METH_NOARGS, fileno_doc},
{"isatty", (PyCFunction)fileio_isatty, METH_NOARGS, isatty_doc},
{"_dealloc_warn", (PyCFunction)fileio_dealloc_warn, METH_O, NULL},
{NULL, NULL} /* sentinel */
};

View file

@ -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,13 @@ textiowrapper_close(textio *self, PyObject *args)
Py_RETURN_NONE; /* stream already closed */
}
else {
if (self->deallocating) {
res = PyObject_CallMethod(self->buffer, "_dealloc_warn", "O", self);
if (res)
Py_DECREF(res);
else
PyErr_Clear();
}
res = PyObject_CallMethod((PyObject *)self, "flush", NULL);
if (res == NULL) {
return NULL;