@@ -40,11 +40,11 @@ check_matched(PyObject *obj, PyObject *arg)
4040 A NULL return value can mean false or an error.
4141*/
4242static PyObject *
43- get_warnings_attr (const char * attr )
43+ get_warnings_attr (const char * attr , int try_import )
4444{
4545 static PyObject * warnings_str = NULL ;
4646 PyObject * all_modules ;
47- PyObject * warnings_module ;
47+ PyObject * warnings_module , * obj ;
4848 int result ;
4949
5050 if (warnings_str == NULL ) {
@@ -53,15 +53,34 @@ get_warnings_attr(const char *attr)
5353 return NULL ;
5454 }
5555
56- all_modules = PyImport_GetModuleDict ();
57- result = PyDict_Contains (all_modules , warnings_str );
58- if (result == -1 || result == 0 )
56+ /* don't try to import after the start of the Python finallization */
57+ if (try_import && _Py_Finalizing == NULL ) {
58+ warnings_module = PyImport_Import (warnings_str );
59+ if (warnings_module == NULL ) {
60+ /* Fallback to the C implementation if we cannot get
61+ the Python implementation */
62+ PyErr_Clear ();
63+ return NULL ;
64+ }
65+ }
66+ else {
67+ all_modules = PyImport_GetModuleDict ();
68+ result = PyDict_Contains (all_modules , warnings_str );
69+ if (result == -1 || result == 0 )
70+ return NULL ;
71+
72+ warnings_module = PyDict_GetItem (all_modules , warnings_str );
73+ Py_INCREF (warnings_module );
74+ }
75+
76+ if (!PyObject_HasAttrString (warnings_module , attr )) {
77+ Py_DECREF (warnings_module );
5978 return NULL ;
79+ }
6080
61- warnings_module = PyDict_GetItem (all_modules , warnings_str );
62- if (!PyObject_HasAttrString (warnings_module , attr ))
63- return NULL ;
64- return PyObject_GetAttrString (warnings_module , attr );
81+ obj = PyObject_GetAttrString (warnings_module , attr );
82+ Py_DECREF (warnings_module );
83+ return obj ;
6584}
6685
6786
@@ -70,7 +89,7 @@ get_once_registry(void)
7089{
7190 PyObject * registry ;
7291
73- registry = get_warnings_attr ("onceregistry" );
92+ registry = get_warnings_attr ("onceregistry" , 0 );
7493 if (registry == NULL ) {
7594 if (PyErr_Occurred ())
7695 return NULL ;
@@ -87,7 +106,7 @@ get_default_action(void)
87106{
88107 PyObject * default_action ;
89108
90- default_action = get_warnings_attr ("defaultaction" );
109+ default_action = get_warnings_attr ("defaultaction" , 0 );
91110 if (default_action == NULL ) {
92111 if (PyErr_Occurred ()) {
93112 return NULL ;
@@ -110,7 +129,7 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
110129 Py_ssize_t i ;
111130 PyObject * warnings_filters ;
112131
113- warnings_filters = get_warnings_attr ("filters" );
132+ warnings_filters = get_warnings_attr ("filters" , 0 );
114133 if (warnings_filters == NULL ) {
115134 if (PyErr_Occurred ())
116135 return NULL ;
@@ -366,7 +385,10 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
366385{
367386 PyObject * show_fn , * msg , * res , * warnmsg_cls = NULL ;
368387
369- show_fn = get_warnings_attr ("_showwarnmsg" );
388+ /* If the source parameter is set, try to get the Python implementation.
389+ The Python implementation is able to log the traceback where the source
390+ was allocated, whereas the C implementation doesnt. */
391+ show_fn = get_warnings_attr ("_showwarnmsg" , source != NULL );
370392 if (show_fn == NULL ) {
371393 if (PyErr_Occurred ())
372394 return -1 ;
@@ -380,7 +402,7 @@ call_show_warning(PyObject *category, PyObject *text, PyObject *message,
380402 goto error ;
381403 }
382404
383- warnmsg_cls = get_warnings_attr ("WarningMessage" );
405+ warnmsg_cls = get_warnings_attr ("WarningMessage" , 0 );
384406 if (warnmsg_cls == NULL ) {
385407 PyErr_SetString (PyExc_RuntimeError ,
386408 "unable to get warnings.WarningMessage" );
0 commit comments