Index: Python/_warnings.c =================================================================== --- Python/_warnings.c (revision 88318) +++ Python/_warnings.c (working copy) @@ -409,10 +409,10 @@ else { PyObject *res; - if (!PyMethod_Check(show_fxn) && !PyFunction_Check(show_fxn)) { + if (!PyCallable_Check(show_fxn)) { PyErr_SetString(PyExc_TypeError, "warnings.showwarning() must be set to a " - "function or method"); + "callable"); Py_DECREF(show_fxn); goto cleanup; } Index: Lib/test/test_warnings.py =================================================================== --- Lib/test/test_warnings.py (revision 88318) +++ Lib/test/test_warnings.py (working copy) @@ -519,6 +519,20 @@ finally: self.module.showwarning = old_showwarning + @support.cpython_only + def test_showwarning_from_c(self): + #Issue #10271: test we can override showarning() with a PyCFunction + from _testcapi import showwarning_from_c + with original_warnings.catch_warnings(module=self.module): + self.module.filterwarnings("always", category=UserWarning) + old_showwarning = self.module.showwarning + self.module.showwarning = showwarning_from_c + try: + self.assertRaisesRegex(Exception, "not an error", + self.module.warn, "test") + finally: + self.module.showwarning = old_showwarning + def test_show_warning_output(self): # With showarning() missing, make sure that output is okay. text = 'test show_warning' Index: Modules/_testcapimodule.c =================================================================== --- Modules/_testcapimodule.c (revision 88318) +++ Modules/_testcapimodule.c (working copy) @@ -2246,6 +2246,33 @@ return NULL; } +/* Issue #10271: test we can override warnings.showwarning() with a PyCFunction. + Run via Lib/test/test_warnings.py */ +static PyObject * +showwarning_from_c(PyObject *self, PyObject *args) +{ + PyObject *message, *category, *filename, *lineno; + PyObject *tmp; + + if (!PyArg_ParseTuple(args, "O!OOO:showwarning_from_c", PyExc_Warning, + &message, &category, &filename, &lineno)) { + return NULL; + } + tmp = PyObject_Str(message); + if (tmp) { + if (!PyUnicode_CompareWithASCIIString(tmp, "test")) { + PyErr_SetString(PyExc_Exception, "not an error"); + } + Py_DECREF(tmp); + } + if (PyErr_Occurred()) { + return NULL; + } + else { + Py_RETURN_NONE; + } +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, @@ -2327,6 +2354,7 @@ {"make_exception_with_doc", (PyCFunction)make_exception_with_doc, METH_VARARGS | METH_KEYWORDS}, {"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS}, + {"showwarning_from_c", (PyCFunction)showwarning_from_c, METH_VARARGS}, {NULL, NULL} /* sentinel */ };