diff -r 93b998d47bc0 Doc/c-api/exceptions.rst --- a/Doc/c-api/exceptions.rst Tue Mar 15 11:12:35 2016 +0100 +++ b/Doc/c-api/exceptions.rst Tue Mar 15 14:09:46 2016 +0100 @@ -334,6 +334,14 @@ an error value). .. versionadded:: 3.2 +.. c:function:: int PyErr_ResourceWarning(PyObject *source, Py_ssize_t stack_level, const char *format, ...) + + Function similar to :c:func:`PyErr_WarnFormat`, but *category* is + :exc:`ResourceWarning` and pass *source* to :func:`warnings.showwarning`. + + .. versionadded:: 3.6 + + Querying the error indicator ============================ diff -r 93b998d47bc0 Doc/library/warnings.rst --- a/Doc/library/warnings.rst Tue Mar 15 11:12:35 2016 +0100 +++ b/Doc/library/warnings.rst Tue Mar 15 14:09:46 2016 +0100 @@ -336,7 +336,7 @@ Available Functions sources). -.. function:: showwarning(message, category, filename, lineno, file=None, line=None) +.. function:: showwarning(message, category, filename, lineno, file=None, line=None, \*, source=None) Write a warning to a file. The default implementation calls ``formatwarning(message, category, filename, lineno, line)`` and writes the @@ -346,14 +346,20 @@ Available Functions message; if *line* is not supplied, :func:`showwarning` will try to read the line specified by *filename* and *lineno*. + .. versionchanged:: 3.6 + Added the *source* parameter. -.. function:: formatwarning(message, category, filename, lineno, line=None) + +.. function:: formatwarning(message, category, filename, lineno, line=None, \*, source=None) Format a warning the standard way. This returns a string which may contain embedded newlines and ends in a newline. *line* is a line of source code to be included in the warning message; if *line* is not supplied, :func:`formatwarning` will try to read the line specified by *filename* and - *lineno*. + *lineno*. *source* is the object which caused the warning. + + .. versionchanged:: 3.6 + Added the *source* parameter. .. function:: filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False) diff -r 93b998d47bc0 Include/warnings.h --- a/Include/warnings.h Tue Mar 15 11:12:35 2016 +0100 +++ b/Include/warnings.h Tue Mar 15 14:09:46 2016 +0100 @@ -17,6 +17,13 @@ PyAPI_FUNC(int) PyErr_WarnFormat( Py_ssize_t stack_level, const char *format, /* ASCII-encoded string */ ...); + +/* Emit a ResourceWarning warning */ +PyAPI_FUNC(int) PyErr_ResourceWarning( + PyObject *source, + Py_ssize_t stack_level, + const char *format, /* ASCII-encoded string */ + ...); #ifndef Py_LIMITED_API PyAPI_FUNC(int) PyErr_WarnExplicitObject( PyObject *category, diff -r 93b998d47bc0 Lib/warnings.py --- a/Lib/warnings.py Tue Mar 15 11:12:35 2016 +0100 +++ b/Lib/warnings.py Tue Mar 15 14:09:46 2016 +0100 @@ -2,24 +2,29 @@ import sys + __all__ = ["warn", "warn_explicit", "showwarning", "formatwarning", "filterwarnings", "simplefilter", "resetwarnings", "catch_warnings"] -def showwarning(message, category, filename, lineno, file=None, line=None): +def showwarning(message, category, filename, lineno, file=None, line=None, + *, source=None): """Hook to write a warning to a file; replace if you like.""" if file is None: file = sys.stderr if file is None: # sys.stderr is None when run with pythonw.exe - warnings get lost return + text = formatwarning(message, category, filename, lineno, line, + source=source) try: - file.write(formatwarning(message, category, filename, lineno, line)) + file.write(text) except OSError: pass # the file (probably stderr) is invalid - this warning gets lost. -def formatwarning(message, category, filename, lineno, line=None): +def formatwarning(message, category, filename, lineno, line=None, + *, source=None): """Function to format a warning the standard way.""" import linecache s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message) @@ -27,6 +32,18 @@ def formatwarning(message, category, fil if line: line = line.strip() s += " %s\n" % line + if source is not None: + import tracemalloc + tb = tracemalloc.get_object_traceback(source) + if tb is not None: + s += 'Object allocated at (most recent call first):\n' + for frame in tb: + s += (' File "%s", lineno %s\n' + % (frame.filename, frame.lineno)) + line = linecache.getline(frame.filename, frame.lineno) + if line: + line = line.strip() + s += ' %s\n' % line return s def filterwarnings(action, message="", category=Warning, module="", lineno=0, diff -r 93b998d47bc0 Modules/_io/fileio.c --- a/Modules/_io/fileio.c Tue Mar 15 11:12:35 2016 +0100 +++ b/Modules/_io/fileio.c Tue Mar 15 14:09:46 2016 +0100 @@ -92,8 +92,7 @@ fileio_dealloc_warn(fileio *self, PyObje 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)) { + if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) { /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) PyErr_WriteUnraisable((PyObject *) self); diff -r 93b998d47bc0 Modules/posixmodule.c --- a/Modules/posixmodule.c Tue Mar 15 11:12:35 2016 +0100 +++ b/Modules/posixmodule.c Tue Mar 15 14:09:46 2016 +0100 @@ -12111,8 +12111,8 @@ ScandirIterator_dealloc(ScandirIterator */ ++Py_REFCNT(iterator); PyErr_Fetch(&exc, &val, &tb); - if (PyErr_WarnFormat(PyExc_ResourceWarning, 1, - "unclosed scandir iterator %R", iterator)) { + if (PyErr_ResourceWarning((PyObject *)iterator, 1, + "unclosed scandir iterator %R", iterator)) { /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) PyErr_WriteUnraisable((PyObject *) iterator); diff -r 93b998d47bc0 Modules/socketmodule.c --- a/Modules/socketmodule.c Tue Mar 15 11:12:35 2016 +0100 +++ b/Modules/socketmodule.c Tue Mar 15 14:09:46 2016 +0100 @@ -4170,8 +4170,7 @@ sock_dealloc(PySocketSockObject *s) 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)) + if (PyErr_ResourceWarning(s, 1, "unclosed %R", s)) /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) PyErr_WriteUnraisable((PyObject *) s); diff -r 93b998d47bc0 Python/_warnings.c --- a/Python/_warnings.c Tue Mar 15 11:12:35 2016 +0100 +++ b/Python/_warnings.c Tue Mar 15 14:09:46 2016 +0100 @@ -287,8 +287,8 @@ update_registry(PyObject *registry, PyOb } static void -show_warning(PyObject *filename, int lineno, PyObject *text, PyObject - *category, PyObject *sourceline) +show_warning(PyObject *filename, int lineno, PyObject *text, + PyObject *category, PyObject *sourceline) { PyObject *f_stderr; PyObject *name; @@ -362,7 +362,8 @@ error: static PyObject * warn_explicit(PyObject *category, PyObject *message, PyObject *filename, int lineno, - PyObject *module, PyObject *registry, PyObject *sourceline) + PyObject *module, PyObject *registry, PyObject *sourceline, + PyObject *source) { PyObject *key = NULL, *text = NULL, *result = NULL, *lineno_obj = NULL; PyObject *item = NULL; @@ -477,7 +478,7 @@ warn_explicit(PyObject *category, PyObje show_warning(filename, lineno, text, category, sourceline); } else { - PyObject *res; + PyObject *args, *kwargs, *res; if (!PyCallable_Check(show_fxn)) { PyErr_SetString(PyExc_TypeError, @@ -487,13 +488,32 @@ warn_explicit(PyObject *category, PyObje goto cleanup; } - res = PyObject_CallFunctionObjArgs(show_fxn, message, category, - filename, lineno_obj, - NULL); + args = Py_BuildValue("(OOOO)", message, category, filename, + lineno_obj); + if (args == NULL) { + Py_DECREF(show_fxn); + goto cleanup; + } + + if (source != NULL) { + kwargs = Py_BuildValue("{sO}", "source", source); + if (kwargs == NULL) { + Py_DECREF(show_fxn); + Py_DECREF(args); + goto cleanup; + } + } + else { + kwargs = NULL; + } + + res = PyObject_Call(show_fxn, args, kwargs); Py_DECREF(show_fxn); - Py_XDECREF(res); + Py_DECREF(args); + Py_XDECREF(kwargs); if (res == NULL) goto cleanup; + Py_DECREF(res); } } else /* if (rc == -1) */ @@ -738,7 +758,8 @@ get_category(PyObject *message, PyObject } static PyObject * -do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level) +do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, + PyObject *source) { PyObject *filename, *module, *registry, *res; int lineno; @@ -747,7 +768,7 @@ do_warn(PyObject *message, PyObject *cat return NULL; res = warn_explicit(category, message, filename, lineno, module, registry, - NULL); + NULL, source); Py_DECREF(filename); Py_DECREF(registry); Py_DECREF(module); @@ -768,14 +789,15 @@ warnings_warn(PyObject *self, PyObject * category = get_category(message, category); if (category == NULL) return NULL; - return do_warn(message, category, stack_level); + return do_warn(message, category, stack_level, NULL); } static PyObject * warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwd_list[] = {"message", "category", "filename", "lineno", - "module", "registry", "module_globals", 0}; + "module", "registry", "module_globals", + "source", 0}; PyObject *message; PyObject *category; PyObject *filename; @@ -783,10 +805,11 @@ warnings_warn_explicit(PyObject *self, P PyObject *module = NULL; PyObject *registry = NULL; PyObject *module_globals = NULL; + PyObject *sourceobj = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOUi|OOO:warn_explicit", + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOUi|OOOO:warn_explicit", kwd_list, &message, &category, &filename, &lineno, &module, - ®istry, &module_globals)) + ®istry, &module_globals, &sourceobj)) return NULL; if (module_globals) { @@ -842,14 +865,14 @@ warnings_warn_explicit(PyObject *self, P /* Handle the warning. */ returned = warn_explicit(category, message, filename, lineno, module, - registry, source_line); + registry, source_line, sourceobj); Py_DECREF(source_list); return returned; } standard_call: return warn_explicit(category, message, filename, lineno, module, - registry, NULL); + registry, NULL, sourceobj); } static PyObject * @@ -864,14 +887,14 @@ warnings_filters_mutated(PyObject *self, static int warn_unicode(PyObject *category, PyObject *message, - Py_ssize_t stack_level) + Py_ssize_t stack_level, PyObject *source) { PyObject *res; if (category == NULL) category = PyExc_RuntimeWarning; - res = do_warn(message, category, stack_level); + res = do_warn(message, category, stack_level, source); if (res == NULL) return -1; Py_DECREF(res); @@ -879,12 +902,28 @@ warn_unicode(PyObject *category, PyObjec return 0; } +static int +_PyErr_WarnFormatV(PyObject *source, + PyObject *category, Py_ssize_t stack_level, + const char *format, va_list vargs) +{ + PyObject *message; + int res; + + message = PyUnicode_FromFormatV(format, vargs); + if (message == NULL) + return -1; + + res = warn_unicode(category, message, stack_level, source); + Py_DECREF(message); + return res; +} + int PyErr_WarnFormat(PyObject *category, Py_ssize_t stack_level, const char *format, ...) { - int ret; - PyObject *message; + int res; va_list vargs; #ifdef HAVE_STDARG_PROTOTYPES @@ -892,25 +931,38 @@ PyErr_WarnFormat(PyObject *category, Py_ #else va_start(vargs); #endif - message = PyUnicode_FromFormatV(format, vargs); - if (message != NULL) { - ret = warn_unicode(category, message, stack_level); - Py_DECREF(message); - } - else - ret = -1; + res = _PyErr_WarnFormatV(NULL, category, stack_level, format, vargs); va_end(vargs); - return ret; + return res; } int +PyErr_ResourceWarning(PyObject *source, Py_ssize_t stack_level, + const char *format, ...) +{ + int res; + va_list vargs; + +#ifdef HAVE_STDARG_PROTOTYPES + va_start(vargs, format); +#else + va_start(vargs); +#endif + res = _PyErr_WarnFormatV(source, PyExc_ResourceWarning, + stack_level, format, vargs); + va_end(vargs); + return res; +} + + +int PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level) { int ret; PyObject *message = PyUnicode_FromString(text); if (message == NULL) return -1; - ret = warn_unicode(category, message, stack_level); + ret = warn_unicode(category, message, stack_level, NULL); Py_DECREF(message); return ret; } @@ -936,7 +988,7 @@ PyErr_WarnExplicitObject(PyObject *categ if (category == NULL) category = PyExc_RuntimeWarning; res = warn_explicit(category, message, filename, lineno, - module, registry, NULL); + module, registry, NULL, NULL); if (res == NULL) return -1; Py_DECREF(res); @@ -1000,7 +1052,7 @@ PyErr_WarnExplicitFormat(PyObject *categ if (message != NULL) { PyObject *res; res = warn_explicit(category, message, filename, lineno, - module, registry, NULL); + module, registry, NULL, NULL); Py_DECREF(message); if (res != NULL) { Py_DECREF(res);