(original) (raw)

changeset: 65707:cff78ffb932a parent: 65705:050f0f7be11e user: Antoine Pitrou solipsis@pitrou.net date: Fri Oct 29 10:38:18 2010 +0000 files: Lib/socket.py Lib/test/test_io.py Lib/test/test_socket.py Lib/xml/etree/ElementTree.py Misc/NEWS Modules/_elementtree.c Modules/_io/bufferedio.c Modules/_io/fileio.c Modules/_io/textio.c Modules/socketmodule.c description: 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. diff -r 050f0f7be11e -r cff78ffb932a Lib/socket.py --- a/Lib/socket.py Fri Oct 29 09:08:19 2010 +0000 +++ b/Lib/socket.py Fri Oct 29 10:38:18 2010 +0000 @@ -108,7 +108,7 @@ if s.startswith("deallocating = 1; if (self->ok && _PyIOBase_finalize((PyObject *) self) < 0) return; _PyObject_GC_UNTRACK(self); @@ -382,6 +384,23 @@ 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 @@ 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 @@ {"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 @@ {"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 @@ {"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 @@ 0, /* tp_alloc */ PyType_GenericNew, /* tp_new */ }; - diff -r 050f0f7be11e -r cff78ffb932a Modules/_io/fileio.c --- a/Modules/_io/fileio.c Fri Oct 29 09:08:19 2010 +0000 +++ b/Modules/_io/fileio.c Fri Oct 29 10:38:18 2010 +0000 @@ -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 @@ 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 @@ 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 @@ 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 @@ static void fileio_dealloc(fileio *self) { + self->deallocating = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; _PyObject_GC_UNTRACK(self); @@ -1008,6 +1038,7 @@ {"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 */ }; diff -r 050f0f7be11e -r cff78ffb932a Modules/_io/textio.c --- a/Modules/_io/textio.c Fri Oct 29 09:08:19 2010 +0000 +++ b/Modules/_io/textio.c Fri Oct 29 10:38:18 2010 +0000 @@ -658,6 +658,7 @@ 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 @@ static void textiowrapper_dealloc(textio *self) { + self->deallocating = 1; if (_textiowrapper_clear(self) < 0) return; _PyObject_GC_UNTRACK(self); @@ -2410,6 +2412,13 @@ 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; diff -r 050f0f7be11e -r cff78ffb932a Modules/socketmodule.c --- a/Modules/socketmodule.c Fri Oct 29 09:08:19 2010 +0000 +++ b/Modules/socketmodule.c Fri Oct 29 10:38:18 2010 +0000 @@ -2941,8 +2941,20 @@ static void sock_dealloc(PySocketSockObject *s) { - if (s->sock_fd != -1) + if (s->sock_fd != -1) { + PyObject *exc, *val, *tb; + Py_ssize_t old_refcount = Py_REFCNT(s); + ++Py_REFCNT(s); + PyErr_Fetch(&exc, &val, &tb); + if (PyErr_WarnFormat(PyExc_ResourceWarning, 1, + "unclosed %R", s)) + /* Spurious errors can appear at shutdown */ + if (PyErr_ExceptionMatches(PyExc_Warning)) + PyErr_WriteUnraisable((PyObject *) s); + PyErr_Restore(exc, val, tb); (void) SOCKETCLOSE(s->sock_fd); + Py_REFCNT(s) = old_refcount; + } Py_TYPE(s)->tp_free((PyObject *)s); }/solipsis@pitrou.net