Add a DeprecationWarning for when warnings.showwarning() is set to a function

that lacks support for the new 'line' argument.
This commit is contained in:
Brett Cannon 2008-05-05 05:32:07 +00:00
parent 9ae080ee5a
commit 8a232cc385
5 changed files with 90 additions and 16 deletions

View file

@ -220,7 +220,8 @@ Available Functions
``warnings.showwarning``. ``warnings.showwarning``.
.. versionchanged:: 2.6 .. versionchanged:: 2.6
Added the *line* argument. Added the *line* argument. Implementations that lack the new argument
will trigger a :exc:`DeprecationWarning`.
.. function:: formatwarning(message, category, filename, lineno[, line]) .. function:: formatwarning(message, category, filename, lineno[, line])

View file

@ -461,6 +461,32 @@ class PyWarningsDisplayTests(BaseTest, WarningsDisplayTests):
module = py_warnings module = py_warnings
class ShowwarningDeprecationTests(BaseTest):
"""Test the deprecation of the old warnings.showwarning() API works."""
@staticmethod
def bad_showwarning(message, category, filename, lineno, file=None):
pass
def test_deprecation(self):
# message, category, filename, lineno[, file[, line]]
args = ("message", UserWarning, "file name", 42)
with test_support.catch_warning(self.module):
self.module.filterwarnings("error", category=DeprecationWarning)
self.module.showwarning = self.bad_showwarning
self.assertRaises(DeprecationWarning, self.module.warn_explicit,
*args)
class CShowwarningDeprecationTests(ShowwarningDeprecationTests):
module = c_warnings
class PyShowwarningDeprecationTests(ShowwarningDeprecationTests):
module = py_warnings
def test_main(): def test_main():
py_warnings.onceregistry.clear() py_warnings.onceregistry.clear()
c_warnings.onceregistry.clear() c_warnings.onceregistry.clear()
@ -471,6 +497,8 @@ def test_main():
CWCmdLineTests, PyWCmdLineTests, CWCmdLineTests, PyWCmdLineTests,
_WarningsTests, _WarningsTests,
CWarningsDisplayTests, PyWarningsDisplayTests, CWarningsDisplayTests, PyWarningsDisplayTests,
CShowwarningDeprecationTests,
PyShowwarningDeprecationTests,
) )

View file

@ -3,6 +3,7 @@
# Note: function level imports should *not* be used # Note: function level imports should *not* be used
# in this module as it may cause import lock deadlock. # in this module as it may cause import lock deadlock.
# See bug 683658. # See bug 683658.
import inspect
import linecache import linecache
import sys import sys
import types import types
@ -21,7 +22,7 @@ def warnpy3k(message, category=None, stacklevel=1):
category = DeprecationWarning category = DeprecationWarning
warn(message, category, stacklevel+1) warn(message, category, stacklevel+1)
def showwarning(message, category, filename, lineno, file=None, line=None): def _show_warning(message, category, filename, lineno, file=None, line=None):
"""Hook to write a warning to a file; replace if you like.""" """Hook to write a warning to a file; replace if you like."""
if file is None: if file is None:
file = sys.stderr file = sys.stderr
@ -29,6 +30,9 @@ def showwarning(message, category, filename, lineno, file=None, line=None):
file.write(formatwarning(message, category, filename, lineno, line)) file.write(formatwarning(message, category, filename, lineno, line))
except IOError: except IOError:
pass # the file (probably stderr) is invalid - this warning gets lost. pass # the file (probably stderr) is invalid - this warning gets lost.
# Keep a worrking version around in case the deprecation of the old API is
# triggered.
showwarning = _show_warning
def formatwarning(message, category, filename, lineno, line=None): def formatwarning(message, category, filename, lineno, line=None):
"""Function to format a warning the standard way.""" """Function to format a warning the standard way."""
@ -259,6 +263,15 @@ def warn_explicit(message, category, filename, lineno,
"Unrecognized action (%r) in warnings.filters:\n %s" % "Unrecognized action (%r) in warnings.filters:\n %s" %
(action, item)) (action, item))
# Print message and context # Print message and context
if inspect.isfunction(showwarning):
arg_spec = inspect.getargspec(showwarning)
if 'line' not in arg_spec.args:
showwarning_msg = ("functions overriding warnings.showwarning() "
"must support the 'line' argument")
if message == showwarning_msg:
_show_warning(message, category, filename, lineno)
else:
warn(showwarning_msg, DeprecationWarning)
showwarning(message, category, filename, lineno) showwarning(message, category, filename, lineno)

View file

@ -42,7 +42,9 @@ Extension Modules
machinery in such places as the parser where use of pure Python code is not machinery in such places as the parser where use of pure Python code is not
possible. Both the ``showarning()`` and ``formatwarning()`` gain an possible. Both the ``showarning()`` and ``formatwarning()`` gain an
optional 'line' argument which is not called by default for optional 'line' argument which is not called by default for
backwards-compatibility reasons. backwards-compatibility reasons. Setting ``warnings.showwarning()`` to
an implementation that lacks support for the ``line`` argument will raise a
DeprecationWarning.
Library Library
------- -------

View file

@ -229,8 +229,8 @@ static void
show_warning(PyObject *filename, int lineno, PyObject *text, PyObject show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
*category, PyObject *sourceline) *category, PyObject *sourceline)
{ {
PyObject *f_stderr; PyObject *f_stderr;
PyObject *name; PyObject *name;
char lineno_str[128]; char lineno_str[128];
PyOS_snprintf(lineno_str, sizeof(lineno_str), ":%d: ", lineno); PyOS_snprintf(lineno_str, sizeof(lineno_str), ":%d: ", lineno);
@ -272,7 +272,7 @@ show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
} }
static PyObject * static PyObject *
warn_explicit(PyObject *category, PyObject *message, warn_explicit(PyObject *category, PyObject *message,
PyObject *filename, int lineno, PyObject *filename, int lineno,
PyObject *module, PyObject *registry, PyObject *sourceline) PyObject *module, PyObject *registry, PyObject *sourceline)
{ {
@ -347,12 +347,12 @@ warn_explicit(PyObject *category, PyObject *message,
goto cleanup; goto cleanup;
} }
/* _once_registry[(text, category)] = 1 */ /* _once_registry[(text, category)] = 1 */
rc = update_registry(registry, text, category, 0); rc = update_registry(registry, text, category, 0);
} }
else if (strcmp(action, "module") == 0) { else if (strcmp(action, "module") == 0) {
/* registry[(text, category, 0)] = 1 */ /* registry[(text, category, 0)] = 1 */
if (registry != NULL) if (registry != NULL)
rc = update_registry(registry, text, category, 0); rc = update_registry(registry, text, category, 0);
} }
else if (strcmp(action, "default") != 0) { else if (strcmp(action, "default") != 0) {
PyObject *to_str = PyObject_Str(item); PyObject *to_str = PyObject_Str(item);
@ -378,15 +378,45 @@ warn_explicit(PyObject *category, PyObject *message,
show_warning(filename, lineno, text, category, sourceline); show_warning(filename, lineno, text, category, sourceline);
} }
else { else {
PyObject *res; const char *msg = "functions overriding warnings.showwarning() "
"must support the 'line' argument";
res = PyObject_CallFunctionObjArgs(show_fxn, message, category, const char *text_char = PyString_AS_STRING(text);
if (strcmp(msg, text_char) == 0) {
/* Prevent infinite recursion by using built-in implementation
of showwarning(). */
show_warning(filename, lineno, text, category, sourceline);
}
else {
PyObject *check_fxn;
PyObject *defaults;
PyObject *res;
if (PyMethod_Check(show_fxn))
check_fxn = PyMethod_Function(show_fxn);
else if (PyFunction_Check(show_fxn))
check_fxn = show_fxn;
else {
PyErr_SetString(PyExc_TypeError,
"warnings.showwarning() must be set to a "
"function or method");
}
defaults = PyFunction_GetDefaults(check_fxn);
/* A proper implementation of warnings.showwarning() should
have at least two default arguments. */
if ((defaults == NULL) || (PyTuple_Size(defaults) < 2)) {
if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1) < 0)
goto cleanup;
}
res = PyObject_CallFunctionObjArgs(show_fxn, message, category,
filename, lineno_obj, filename, lineno_obj,
NULL); NULL);
Py_DECREF(show_fxn); Py_DECREF(show_fxn);
Py_XDECREF(res); Py_XDECREF(res);
if (res == NULL) if (res == NULL)
goto cleanup; goto cleanup;
}
} }
} }
else /* if (rc == -1) */ else /* if (rc == -1) */
@ -578,7 +608,7 @@ warnings_warn(PyObject *self, PyObject *args, PyObject *kwds)
PyObject *message, *category = NULL; PyObject *message, *category = NULL;
Py_ssize_t stack_level = 1; Py_ssize_t stack_level = 1;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|On:warn", kw_list, if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|On:warn", kw_list,
&message, &category, &stack_level)) &message, &category, &stack_level))
return NULL; return NULL;