Index: Python/errors.c =================================================================== --- Python/errors.c (Revision 54712) +++ Python/errors.c (Arbeitskopie) @@ -635,7 +635,8 @@ /* Function to issue a warning message; may raise an exception. */ int -PyErr_WarnEx(PyObject *category, const char *message, Py_ssize_t stack_level) +PyErr_WarnEx2(PyObject *category, const char *message, + Py_ssize_t stack_level, const char *skip_modules) { PyObject *dict, *func = NULL; PyObject *warnings_module = PyModule_GetWarningsModule(); @@ -651,11 +652,21 @@ } else { PyObject *res; + PyObject *smo; if (category == NULL) category = PyExc_RuntimeWarning; + if (skip_modules == NULL) { + smo = Py_None; + Py_INCREF(smo); + } + else { + if ((smo = PyString_FromString(skip_modules)) == NULL) + return -1; + } res = PyObject_CallFunction(func, "sOn", - message, category, stack_level); + message, category, stack_level, smo); + Py_DECREF(smo); if (res == NULL) return -1; Py_DECREF(res); @@ -663,15 +674,25 @@ } } -/* PyErr_Warn is only for backwards compatability and will be removed. - Use PyErr_WarnEx instead. */ +/* PyErr_WarnEx is only for backwards compatability and will be removed. + Use PyErr_WarnEx2 instead. */ +#undef PyErr_WarnEx #undef PyErr_Warn PyAPI_FUNC(int) +PyErr_WarnEx(PyObject *category, char *message, Py_ssize_t stack_level) +{ + return PyErr_WarnEx2(category, message, stack_level, NULL); +} + +/* PyErr_Warn is only for backwards compatability and will be removed. + Use PyErr_WarnEx instead. */ + +PyAPI_FUNC(int) PyErr_Warn(PyObject *category, char *message) { - return PyErr_WarnEx(category, message, 1); + return PyErr_WarnEx2(category, message, 1, NULL); } /* Warning with explicit origin */ Index: Include/pyerrors.h =================================================================== --- Include/pyerrors.h (Revision 54712) +++ Include/pyerrors.h (Arbeitskopie) @@ -112,7 +112,7 @@ ? (PyObject*)((PyInstanceObject*)(x))->in_class \ : (PyObject*)((x)->ob_type))) - + /* Predefined exceptions */ PyAPI_DATA(PyObject *) PyExc_BaseException; @@ -224,15 +224,20 @@ PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *); /* Issue a warning or exception */ -PyAPI_FUNC(int) PyErr_WarnEx(PyObject *category, const char *msg, - Py_ssize_t stack_level); +PyAPI_FUNC(int) PyErr_WarnEx2(PyObject *category, const char *msg, + Py_ssize_t stack_level, const char *skip_modules); PyAPI_FUNC(int) PyErr_WarnExplicit(PyObject *, const char *, - const char *, int, + const char *, int, const char *, PyObject *); -/* PyErr_Warn is only for backwards compatability and will be removed. - Use PyErr_WarnEx instead. */ -#define PyErr_Warn(category, msg) PyErr_WarnEx(category, msg, 1) +/* PyErr_WarnEx is only for backwards compatibility and will be removed. + Use PyErr_WarnEx2 instead. */ +#define PyErr_WarnEx(category, msg, stack_level) \ + PyErr_WarnEx(category, msg, stack_level, NULL) +/* PyErr_Warn is only for backwards compatibility and will be removed. + Use PyErr_WarnEx2 instead. */ +#define PyErr_Warn(category, msg) PyErr_WarnEx2(category, msg, 1, NULL) + /* In sigcheck.c or signalmodule.c */ PyAPI_FUNC(int) PyErr_CheckSignals(void); PyAPI_FUNC(void) PyErr_SetInterrupt(void); Index: Doc/lib/libwarnings.tex =================================================================== --- Doc/lib/libwarnings.tex (Revision 54712) +++ Doc/lib/libwarnings.tex (Arbeitskopie) @@ -165,7 +165,8 @@ \subsection{Available Functions \label{warning-functions}} -\begin{funcdesc}{warn}{message\optional{, category\optional{, stacklevel}}} +\begin{funcdesc}{warn}{message\optional{, category\optional{, + stacklevel\optional{, skipmodules}}}} Issue a warning, or maybe ignore it or raise an exception. The \var{category} argument, if given, must be a warning category class (see above); it defaults to \exception{UserWarning}. Alternatively @@ -185,6 +186,11 @@ This makes the warning refer to \function{deprecation()}'s caller, rather than to the source of \function{deprecation()} itself (since the latter would defeat the purpose of the warning message). + +The \var{skipmodules} argument is a regular expression. A module whose +name matches \var{skipmodules} will be ignored when determining the +stacklevel to use in the report. +\versionchanged[\var{skipmodules} argument added]{2.6} \end{funcdesc} \begin{funcdesc}{warn_explicit}{message, category, filename, Index: Doc/api/exceptions.tex =================================================================== --- Doc/api/exceptions.tex (Revision 54712) +++ Doc/api/exceptions.tex (Arbeitskopie) @@ -259,7 +259,8 @@ argument. It is mostly for internal use. \end{cfuncdesc} -\begin{cfuncdesc}{int}{PyErr_WarnEx}{PyObject *category, char *message, int stacklevel} +\begin{cfuncdesc}{int}{PyErr_WarnEx2}{PyObject *category, char *message, + int stacklevel, char *skipmodules} Issue a warning message. The \var{category} argument is a warning category (see below) or \NULL; the \var{message} argument is a message string. \var{stacklevel} is a positive number giving a @@ -268,6 +269,10 @@ of 1 is the function calling \cfunction{PyErr_WarnEx()}, 2 is the function above that, and so forth. + If \var{skipmodules} is not \code{NULL}, it will be treated as a regular + expression. All modules whose name matches \var{skipmodules} will be + ignored when determining the stacklevel to use in the report. + This function normally prints a warning message to \var{sys.stderr}; however, it is also possible that the user has specified that warnings are to be turned into errors, and in that case this will @@ -297,16 +302,30 @@ For information about warning control, see the documentation for the \module{warnings} module and the \programopt{-W} option in the command line documentation. There is no C API for warning control. + + \versionchanged[\var{skipmodules} argument added]{2.6} \end{cfuncdesc} +\begin{cfuncdesc}{int}{PyErr_WarnEx}{PyObject *category, char *message, int stacklevel} + Issue a warning message. The \var{category} argument is a warning + category (see below) or \NULL; the \var{message} argument is a + message string. \var{stacklevel} is a positive number giving a + number of stack frames. The warning will appear to be issued from + the function calling \cfunction{PyErr_WarnEx()}, equivalent to calling + \cfunction{PyErr_WarnEx2()} with \var{skipmodules} set to \code{NULL}. + + Deprecated; use \cfunction{PyErr_WarnEx2()} instead. +\end{cfuncdesc} + \begin{cfuncdesc}{int}{PyErr_Warn}{PyObject *category, char *message} Issue a warning message. The \var{category} argument is a warning category (see below) or \NULL; the \var{message} argument is a message string. The warning will appear to be issued from the function calling \cfunction{PyErr_Warn()}, equivalent to calling - \cfunction{PyErr_WarnEx()} with a \var{stacklevel} of 1. + \cfunction{PyErr_WarnEx2()} with a \var{stacklevel} of 1 and + \var{skipmodules} set to \code{NULL}. - Deprecated; use \cfunction{PyErr_WarnEx()} instead. + Deprecated; use \cfunction{PyErr_WarnEx2()} instead. \end{cfuncdesc} \begin{cfuncdesc}{int}{PyErr_WarnExplicit}{PyObject *category, Index: Lib/warnings.py =================================================================== --- Lib/warnings.py (Revision 54712) +++ Lib/warnings.py (Arbeitskopie) @@ -21,28 +21,48 @@ defaultaction = "default" onceregistry = {} -def warn(message, category=None, stacklevel=1): - """Issue a warning, or maybe ignore it or raise an exception.""" - # Check if message is already a Warning object - if isinstance(message, Warning): - category = message.__class__ - # Check category argument - if category is None: - category = UserWarning - assert issubclass(category, Warning) +def _stackinfo(stacklevel): # Get context information try: - caller = sys._getframe(stacklevel) + caller = sys._getframe(stacklevel+1) except ValueError: globals = sys.__dict__ lineno = 1 + real = False else: globals = caller.f_globals lineno = caller.f_lineno + real = True if '__name__' in globals: module = globals['__name__'] else: module = "" + return (module, globals, lineno, real) + +def warn(message, category=None, stacklevel=1, skipmodules=None): + """Issue a warning, or maybe ignore it or raise an exception.""" + # Check if message is already a Warning object + if isinstance(message, Warning): + category = message.__class__ + # Check category argument + if category is None: + category = UserWarning + assert issubclass(category, Warning) + # Get context information + if skipmodules is None: + (module, globals, lineno, real) = _stackinfo(stacklevel) + else: + import re + reallevel = 1 + while True: + (module, globals, lineno, real) = _stackinfo(reallevel) + if not real: + break + if not re.match(skipmodules, module): + if stacklevel == 1: + break + stacklevel -= 1 + reallevel += 1 filename = globals.get('__file__') if filename: fnl = filename.lower() Index: Lib/test/test_warnings.py =================================================================== --- Lib/test/test_warnings.py (Revision 54712) +++ Lib/test/test_warnings.py (Arbeitskopie) @@ -95,7 +95,21 @@ warning_tests.inner("spam7", stacklevel=9999) self.assertEqual(os.path.basename(w.filename), "sys") + def test_skipmodules(self): + # Test stacklevel+skipmodules argument + with test_support.catch_warning() as w: + warning_tests.inner("spam8", stacklevel=1, skipmodules=r"(test\.|)warning_tests") + self.assertEqual(os.path.basename(w.filename), "test_warnings.py") + warning_tests.outer("spam9", stacklevel=1, skipmodules=r"(test\.|)warning_tests") + self.assertEqual(os.path.basename(w.filename), "test_warnings.py") + warning_tests.inner("spam10", stacklevel=2, skipmodules=r"(test\.|)warning_tests") + self.assertEqual(os.path.basename(w.filename), "unittest.py") + warning_tests.outer("spam11", stacklevel=2, skipmodules=r"(test\.|)warning_tests") + self.assertEqual(os.path.basename(w.filename), "unittest.py") + self.assertEqual(str(w.message), "spam11") + + def test_main(verbose=None): # Obscure hack so that this test passes after reloads or repeated calls # to test_main (regrtest -R). Index: Lib/test/warning_tests.py =================================================================== --- Lib/test/warning_tests.py (Revision 54712) +++ Lib/test/warning_tests.py (Arbeitskopie) @@ -2,8 +2,8 @@ import warnings -def outer(message, stacklevel=1): - inner(message, stacklevel) +def outer(message, stacklevel=1, skipmodules=None): + inner(message, stacklevel, skipmodules=skipmodules) -def inner(message, stacklevel=1): - warnings.warn(message, stacklevel=stacklevel) +def inner(message, stacklevel=1, skipmodules=None): + warnings.warn(message, stacklevel=stacklevel, skipmodules=skipmodules)