From: Victor Stinner Date: Tue, 27 Jun 2017 12:31:40 +0000 (+0200) Subject: bpo-30764: Windows support.SuppressCrashReport (#2423) X-Git-Tag: v2.7.14rc1~76 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=95a91df0363f88722ea2860d9d42a0b04b3f8be2;p=python bpo-30764: Windows support.SuppressCrashReport (#2423) * Add Windows support to test.support.SuppressCrashReport: call SetErrorMode() and CrtSetReportMode(). * _testcapi: add CrtSetReportMode() and CrtSetReportFile() functions and CRT_xxx and CRTDBG_xxx constants needed by SuppressCrashReport. --- diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1f280bc7be..3b9eb045ff 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1863,8 +1863,34 @@ class SuppressCrashReport: soft limit to 0. """ if sys.platform.startswith('win'): - # TODO: backport the Windows implementation - pass + # see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621.aspx + # GetErrorMode is not available on Windows XP and Windows Server 2003, + # but SetErrorMode returns the previous value, so we can use that + import ctypes + self._k32 = ctypes.windll.kernel32 + SEM_NOGPFAULTERRORBOX = 0x02 + self.old_value = self._k32.SetErrorMode(SEM_NOGPFAULTERRORBOX) + self._k32.SetErrorMode(self.old_value | SEM_NOGPFAULTERRORBOX) + + # Suppress assert dialogs in debug builds + # (see http://bugs.python.org/issue23314) + try: + import _testcapi + _testcapi.CrtSetReportMode + except (AttributeError, ImportError): + # no _testcapi or a release build + pass + else: + self.old_modes = {} + for report_type in [_testcapi.CRT_WARN, + _testcapi.CRT_ERROR, + _testcapi.CRT_ASSERT]: + old_mode = _testcapi.CrtSetReportMode(report_type, + _testcapi.CRTDBG_MODE_FILE) + old_file = _testcapi.CrtSetReportFile(report_type, + _testcapi.CRTDBG_FILE_STDERR) + self.old_modes[report_type] = old_mode, old_file + else: try: import resource @@ -1906,16 +1932,16 @@ class SuppressCrashReport: return if sys.platform.startswith('win'): - # TODO: backport the Windows implementation - pass + self._k32.SetErrorMode(self.old_value) + + if self.old_modes: + import _testcapi + for report_type, (old_mode, old_file) in self.old_modes.items(): + _testcapi.CrtSetReportMode(report_type, old_mode) + _testcapi.CrtSetReportFile(report_type, old_file) else: + import resource try: - import resource - except ImportError: - resource = None - - if resource is not None: - try: - resource.setrlimit(resource.RLIMIT_CORE, self.old_value) - except (ValueError, OSError): - pass + resource.setrlimit(resource.RLIMIT_CORE, self.old_value) + except (ValueError, OSError): + pass diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index e742541ee6..a68d839a7f 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -11,6 +11,9 @@ #include "datetime.h" #include "marshal.h" #include +#ifdef MS_WINDOWS +# include +#endif #ifdef WITH_THREAD #include "pythread.h" @@ -2501,6 +2504,42 @@ test_raise_signal(PyObject* self, PyObject *args) } +#ifdef MS_WINDOWS +static PyObject* +msvcrt_CrtSetReportMode(PyObject* self, PyObject *args) +{ + int type, mode; + int res; + + if (!PyArg_ParseTuple(args, "ii:CrtSetReportMode", &type, &mode)) { + return NULL; + } + + res = _CrtSetReportMode(type, mode); + if (res == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return PyInt_FromLong(res); +} + +static PyObject* +msvcrt_CrtSetReportFile(PyObject* self, PyObject *args) +{ + int type, file; + long res; + + if (!PyArg_ParseTuple(args, "ii:CrtSetReportFile", &type, &file)) { + return NULL; + } + + res = (long)_CrtSetReportFile(type, (_HFILE)file); + + return PyInt_FromLong(res); +} +#endif + + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"set_errno", set_errno, METH_VARARGS}, @@ -2613,6 +2652,10 @@ static PyMethodDef TestMethods[] = { {"pymarshal_read_object_from_file", pymarshal_read_object_from_file, METH_VARARGS}, {"raise_signal", (PyCFunction)test_raise_signal, METH_VARARGS}, +#ifdef MS_WINDOWS + {"CrtSetReportMode", (PyCFunction)msvcrt_CrtSetReportMode, METH_VARARGS}, + {"CrtSetReportFile", (PyCFunction)msvcrt_CrtSetReportFile, METH_VARARGS}, +#endif {NULL, NULL} /* sentinel */ }; @@ -2809,6 +2852,14 @@ init_testcapi(void) PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyInt_FromSsize_t(PY_SSIZE_T_MIN)); PyModule_AddObject(m, "SIZEOF_PYGC_HEAD", PyInt_FromSsize_t(sizeof(PyGC_Head))); +#ifdef MS_WINDOWS + PyModule_AddIntConstant(m, "CRT_WARN", _CRT_WARN); + PyModule_AddIntConstant(m, "CRT_ERROR", _CRT_ERROR); + PyModule_AddIntConstant(m, "CRT_ASSERT", _CRT_ASSERT); + PyModule_AddIntConstant(m, "CRTDBG_MODE_FILE", _CRTDBG_MODE_FILE); + PyModule_AddIntConstant(m, "CRTDBG_FILE_STDERR", (int)_CRTDBG_FILE_STDERR); +#endif + TestError = PyErr_NewException("_testcapi.error", NULL, NULL); Py_INCREF(TestError); PyModule_AddObject(m, "error", TestError);