You can get what you want by assigning to warnings.showwarning. The warnings module documentation itself recommends that you do that, so it's not that you're being tempted by the dark side of the source. :)
You may replace this function with an alternative implementation by assigning to warnings.showwarning.
You can define a new function that does what warning.showwarning normaly does and additionally it prints the stack. Then you place it instead of the original:
After this, every warning will print the stack trace as well as the warning message. Take into account, however, that if the warning is ignored because it is not the first one, nothing will happen, so you still need to execute:
warnings.simplefilter("always")
You can get a similar control that the one numpy.seterr gives through the warning module's filters
If what you want is python to report every a warning every time it is triggered and not only the first time, you can include something like:
import warnings
warnings.simplefilter("always")
You can get other behaviours by passing different strings as arguments. Using the same function you can also specify different behaviours for warnings depending on the module that raised them, the message they provide, the warning class, the line of code that is causing it and so on...
This way you get the full traceback for each warning raised as error (only the first one, since execution will stop... but you can address them one by one, and create a filter to ignore the ones you don't want to hear about again...
For Python3, see stacklevel parameter in the warnings module documentation. This can be particularly helpful when 3rd party warnings are buried in the call stack: set warnings.warn call parameter to stacklevel=2, see the traceback, make changes where necessary, revert / remove stacklevel to original state.
Options to log the warning with minimal disruption to program flow.
Get traceback without raising an error:
import warnings
import traceback
warnings.filterwarnings("error") # Treat warnings as errors
try:
your_code()
except Warning:
print(traceback.format_exc()) # print traceback
warnings.resetwarnings() # Back to default behavior
If you want to execute the same (or similar) code either way, you could execute it in the except block, this time ignoring the warning, since you already got its traceback:
import warnings
import traceback
warnings.filterwarnings("error") # Treat warnings as errors
try:
your_code()
except Warning:
print(traceback.format_exc()) # print traceback
warnings.filterwarnings("ignore") # ignore warnings
your_code() # Execute either way (same code or alternative code)
warnings.resetwarnings() # Back to default behavior