]> granicus.if.org Git - python/commitdiff
_warnings.warn_explicit(): try to import warnings
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 22 Mar 2016 23:54:48 +0000 (00:54 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Tue, 22 Mar 2016 23:54:48 +0000 (00:54 +0100)
Issue #26592: _warnings.warn_explicit() now tries to import the warnings module
(Python implementation) if the source parameter is set to be able to log the
traceback where the source was allocated.

Python/_warnings.c

index dcac57bda58811261d9f7403b18470f6e4f26a7e..41eaf5310ae66d161c711f32207e4d5363cfbc75 100644 (file)
@@ -40,11 +40,11 @@ check_matched(PyObject *obj, PyObject *arg)
    A NULL return value can mean false or an error.
 */
 static PyObject *
-get_warnings_attr(const char *attr)
+get_warnings_attr(const char *attr, int try_import)
 {
     static PyObject *warnings_str = NULL;
     PyObject *all_modules;
-    PyObject *warnings_module;
+    PyObject *warnings_module, *obj;
     int result;
 
     if (warnings_str == NULL) {
@@ -53,15 +53,34 @@ get_warnings_attr(const char *attr)
             return NULL;
     }
 
-    all_modules = PyImport_GetModuleDict();
-    result = PyDict_Contains(all_modules, warnings_str);
-    if (result == -1 || result == 0)
+    /* don't try to import after the start of the Python finallization */
+    if (try_import && _Py_Finalizing == NULL) {
+        warnings_module = PyImport_Import(warnings_str);
+        if (warnings_module == NULL) {
+            /* Fallback to the C implementation if we cannot get
+               the Python implementation */
+            PyErr_Clear();
+            return NULL;
+        }
+    }
+    else {
+        all_modules = PyImport_GetModuleDict();
+        result = PyDict_Contains(all_modules, warnings_str);
+        if (result == -1 || result == 0)
+            return NULL;
+
+        warnings_module = PyDict_GetItem(all_modules, warnings_str);
+        Py_INCREF(warnings_module);
+    }
+
+    if (!PyObject_HasAttrString(warnings_module, attr)) {
+        Py_DECREF(warnings_module);
         return NULL;
+    }
 
-    warnings_module = PyDict_GetItem(all_modules, warnings_str);
-    if (!PyObject_HasAttrString(warnings_module, attr))
-            return NULL;
-    return PyObject_GetAttrString(warnings_module, attr);
+    obj = PyObject_GetAttrString(warnings_module, attr);
+    Py_DECREF(warnings_module);
+    return obj;
 }
 
 
@@ -70,7 +89,7 @@ get_once_registry(void)
 {
     PyObject *registry;
 
-    registry = get_warnings_attr("onceregistry");
+    registry = get_warnings_attr("onceregistry", 0);
     if (registry == NULL) {
         if (PyErr_Occurred())
             return NULL;
@@ -87,7 +106,7 @@ get_default_action(void)
 {
     PyObject *default_action;
 
-    default_action = get_warnings_attr("defaultaction");
+    default_action = get_warnings_attr("defaultaction", 0);
     if (default_action == NULL) {
         if (PyErr_Occurred()) {
             return NULL;
@@ -110,7 +129,7 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
     Py_ssize_t i;
     PyObject *warnings_filters;
 
-    warnings_filters = get_warnings_attr("filters");
+    warnings_filters = get_warnings_attr("filters", 0);
     if (warnings_filters == NULL) {
         if (PyErr_Occurred())
             return NULL;
@@ -366,7 +385,10 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
 {
     PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL;
 
-    show_fn = get_warnings_attr("_showwarnmsg");
+    /* If the source parameter is set, try to get the Python implementation.
+       The Python implementation is able to log the traceback where the source
+       was allocated, whereas the C implementation doesnt. */
+    show_fn = get_warnings_attr("_showwarnmsg", source != NULL);
     if (show_fn == NULL) {
         if (PyErr_Occurred())
             return -1;
@@ -380,7 +402,7 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
         goto error;
     }
 
-    warnmsg_cls = get_warnings_attr("WarningMessage");
+    warnmsg_cls = get_warnings_attr("WarningMessage", 0);
     if (warnmsg_cls == NULL) {
         PyErr_SetString(PyExc_RuntimeError,
                 "unable to get warnings.WarningMessage");