103

In numpy we can do np.seterr(invalid='raise') to get a traceback for warnings raising an error instead (see this post).

  • Is there a general way for tracing warnings?
  • Can I make python to give a traceback, when a warning is raised?
Community
  • 1
  • 1
embert
  • 7,336
  • 10
  • 49
  • 78
  • The traceback module? http://docs.python.org/2/library/traceback.html – Jayanth Koushik Mar 13 '14 at 09:18
  • I guess none of the answers fully satisfies you; could you explain a bit more what you need? :) – mgab Mar 23 '14 at 16:18
  • @mgab the problem is that it can be hard to locate a warning, since it does not come with information, where it was invoked. Making all warnings fatal, the program will exit after the first warning, that was invoked *somewhere*. I was wondering, if there is a way, to tell python to print a traceback when a warning is raised or any other way to determine the origin of a warning. – embert Mar 24 '14 at 11:51
  • Well, if the warning is raised as an error, it will indeed stop at the first one, but you should get a traceback reporting the full stack of where that error (or warning originally) was raised. However I agree that is not optimal, specially if your script takes a while... I updated my answer with what I think is a better addresses your question – mgab Mar 24 '14 at 18:22

5 Answers5

140

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:

import traceback
import warnings
import sys

def warn_with_traceback(message, category, filename, lineno, file=None, line=None):

    log = file if hasattr(file,'write') else sys.stderr
    traceback.print_stack(file=log)
    log.write(warnings.formatwarning(message, category, filename, lineno, line))

warnings.showwarning = warn_with_traceback

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...

You can check the list in the module documentation

As an example, you can set all the warnings to raise exceptions, except the DeprecationWarnings that should be ignored completely:

import warnings
warnings.simplefilter("error")
warnings.simplefilter("ignore", DeprecationWarning)

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...

mgab
  • 3,147
  • 4
  • 19
  • 30
  • 4
    Such a useful answer. @embert should really mark it as the answer after all these years. – beldaz Mar 07 '19 at 21:53
  • Really helpful answer, wish I had found it earlier so I wouldn't have to struggle to find the cause of "line buffering (buffering=1) isn't supported in binary mode". – MadHatter Jun 01 '23 at 10:56
47

Run your program like

python -W error myprogram.py

This makes all warnings fatal, see here for more information

Jakob Bowyer
  • 33,878
  • 8
  • 76
  • 91
  • 17
    That works. Is there, however, a way to force python to print a traceback on every warning. The problem is, that using this option the program will already fail, when a deprecation warning during import is raised. – embert Mar 13 '14 at 09:30
  • If you need this is a unit test (or a module), you can do this: python -W error -m pytest – Omry Yadan Aug 31 '19 at 03:57
  • 2
    FYI, pytest understands the `-W` flag and can be run with: `pytest ... -W error` (Docs: https://docs.pytest.org/en/stable/warnings.html). Depending on verbosity, a traceback is shown just like any other test failure – KyleKing Mar 23 '21 at 17:59
  • In case you are running a python program via its command-line interface, you can also set this flag via an environment variable: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS – Ben Farmer Nov 12 '21 at 04:22
5

You can use warnings.filterwarnings() to turn selected warnings into exceptions and get a traceback. Minimal working example as follows:

import warnings
warnings.filterwarnings(
    action='error', message='',
    category=RuntimeWarning
)

In my case:

import warnings
warnings.filterwarnings(
    'error', 'DateTimeField .* received a naive datetime',
    RuntimeWarning, 'django.db.models.fields'
)
Muhammad Yasirroni
  • 1,512
  • 12
  • 22
mrts
  • 16,697
  • 8
  • 89
  • 72
2

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.

Eg.:

warnings.warn("It's dangerous to go alone! Take this.", stacklevel=2)
Voy
  • 5,286
  • 1
  • 49
  • 59
GG_Python
  • 3,436
  • 5
  • 34
  • 46
1

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