]> granicus.if.org Git - python/commitdiff
bpo-30764: Windows support.SuppressCrashReport (#2423)
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 27 Jun 2017 12:31:40 +0000 (14:31 +0200)
committerGitHub <noreply@github.com>
Tue, 27 Jun 2017 12:31:40 +0000 (14:31 +0200)
* 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.

Lib/test/support/__init__.py
Modules/_testcapimodule.c

index 1f280bc7be8cf59023f828f675f8b630abaf214a..3b9eb045ffa7e185df070f3d07b6b218c1137bfd 100644 (file)
@@ -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
index e742541ee6c04485114d79945842ab245c817d26..a68d839a7f7de0b3c6a4ec8ecefa363427bdca0f 100644 (file)
@@ -11,6 +11,9 @@
 #include "datetime.h"
 #include "marshal.h"
 #include <signal.h>
+#ifdef MS_WINDOWS
+#  include <crtdbg.h>
+#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);